Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/misc/dev_tools.py
8814 views
1
r"""
2
Some tools for developers
3
4
AUTHORS:
5
6
- Nicolas M. Thiery: initial version
7
8
- Vincent Delecroix (2012): improve import_statements
9
"""
10
#*****************************************************************************
11
# Copyright (C) 2011 Nicolas M. Thiery <nthiery at users.sf.net>
12
#
13
# Distributed under the terms of the GNU General Public License (GPL)
14
# http://www.gnu.org/licenses/
15
#******************************************************************************
16
17
def module_names_cmp(x,y):
18
r"""
19
A comparison function for module names.
20
21
This function first compares the depth of the modules and then
22
breaks ties by alphabetical order.
23
24
.. SEEALSO:: This function is used in :func:`import_statements`.
25
26
TESTS::
27
28
sage: from sage.misc.dev_tools import module_names_cmp
29
sage: l = ['sage.groups.perm_gps', 'sage.combinat', 'sage.all', 'sage.plot.plot3d']
30
sage: sorted(l, cmp=module_names_cmp)
31
['sage.all', 'sage.combinat', 'sage.groups.perm_gps', 'sage.plot.plot3d']
32
"""
33
test = cmp(x.count('.'), y.count('.'))
34
if test: return test
35
return cmp(x,y)
36
37
def runsnake(command):
38
"""
39
Graphical profiling with ``runsnake``
40
41
INPUT:
42
43
- ``command`` -- the command to be run as a string.
44
45
EXAMPLES::
46
47
sage: runsnake("list(SymmetricGroup(3))") # optional - runsnake
48
49
``command`` is first preparsed (see :func:`preparse`)::
50
51
sage: runsnake('for x in range(1,4): print x^2') # optional - runsnake
52
1
53
4
54
9
55
56
:func:`runsnake` requires the program ``runsnake``. Due to non
57
trivial dependencies (python-wxgtk, ...), installing it within the
58
Sage distribution is unpractical. Hence, we recommend installing
59
it with the system wide Python. On Ubuntu 10.10, this can be done
60
with::
61
62
> sudo apt-get install python-profiler python-wxgtk2.8 python-setuptools
63
> sudo easy_install RunSnakeRun
64
65
See the ``runsnake`` website for instructions for other platforms.
66
67
:func:`runsnake` further assumes that the system wide Python is
68
installed in ``/usr/bin/python``.
69
70
.. seealso::
71
72
- `The runsnake website <http://www.vrplumber.com/programming/runsnakerun/>`_
73
- ``%prun``
74
- :class:`Profiler`
75
76
"""
77
import cProfile, os
78
from sage.misc.misc import tmp_filename, get_main_globals
79
from sage.misc.preparser import preparse
80
tmpfile = tmp_filename()
81
cProfile.runctx(preparse(command.lstrip().rstrip()), get_main_globals(), locals(), filename=tmpfile)
82
os.system("/usr/bin/python -E `which runsnake` %s &"%tmpfile)
83
84
def print_import_statement(module, name, lazy):
85
r"""
86
Print an import statement.
87
88
INPUT:
89
90
- ``module`` -- the name of a module
91
92
- ``name`` -- the name of the object to import
93
94
- ``lazy`` -- a boolean: whether to print a lazy import statement
95
96
EXAMPLES::
97
98
sage: sage.misc.dev_tools.print_import_statement('sage.misc.dev_tools', 'print_import_statement', False)
99
from sage.misc.dev_tools import print_import_statement
100
sage: sage.misc.dev_tools.print_import_statement('sage.misc.dev_tools', 'print_import_statement', True)
101
lazy_import('sage.misc.dev_tools', 'print_import_statement')
102
"""
103
if lazy:
104
print "lazy_import('%s', '%s')"%(module, name)
105
else:
106
print "from %s import %s"%(module, name)
107
108
def import_statements(*objects, **options):
109
"""
110
Print import statements for the given objects.
111
112
INPUT:
113
114
- ``*objects`` -- a sequence of objects.
115
116
- ``lazy`` -- a boolean (default: ``False``)
117
Whether to print a lazy import statement.
118
119
- ``verbose`` -- a boolean (default: ``True``)
120
Whether to print information in case of ambiguity.
121
122
EXAMPLES::
123
124
sage: import_statements(WeylGroup, lazy_attribute)
125
from sage.combinat.root_system.weyl_group import WeylGroup
126
from sage.misc.lazy_attribute import lazy_attribute
127
128
sage: import_statements(IntegerRing)
129
from sage.rings.integer_ring import IntegerRing
130
131
If ``lazy`` is True, then :func:`lazy_import` statements are
132
displayed instead::
133
134
sage: import_statements(WeylGroup, lazy_attribute, lazy=True)
135
from sage.misc.lazy_import import lazy_import
136
lazy_import('sage.combinat.root_system.weyl_group', 'WeylGroup')
137
lazy_import('sage.misc.lazy_attribute', 'lazy_attribute')
138
139
In principle, the function should also work on object which are instances.
140
In case of ambiguity, one or two warning lines are printed::
141
142
sage: import_statements(RDF)
143
from sage.rings.real_double import RDF
144
145
sage: import_statements(ZZ)
146
** Warning **: several names for that object: Z, ZZ
147
from sage.rings.integer_ring import Z
148
149
sage: import_statements(euler_phi)
150
from sage.rings.arith import euler_phi
151
152
sage: import_statements(x)
153
** Warning **: several modules for that object: sage.all_cmdline, sage.calculus.predefined
154
from sage.calculus.predefined import x
155
156
If you don't like the warning you can disable them with the option ``verbose``::
157
158
sage: import_statements(ZZ, verbose=False)
159
from sage.rings.integer_ring import Z
160
161
sage: import_statements(x, verbose=False)
162
from sage.calculus.predefined import x
163
164
If the object has several names, an other way to get the import
165
statement you expect is to use a string instead of the object::
166
167
sage: import_statements(cached_function)
168
** Warning **: several names for that object: CachedFunction, cached_function
169
from sage.misc.cachefunc import CachedFunction
170
171
sage: import_statements('cached_function')
172
from sage.misc.cachefunc import cached_function
173
sage: import_statements('Z')
174
from sage.rings.integer_ring import Z
175
176
177
Specifying a string is also useful for objects that are not
178
imported in the Sage interpreter namespace by default. In this
179
case, an object with that name is looked up in all the modules
180
that have been imported in this session::
181
182
sage: print_import_statement
183
Traceback (most recent call last):
184
...
185
NameError: name 'print_import_statement' is not defined
186
187
sage: import_statements("print_import_statement")
188
from sage.misc.dev_tools import print_import_statement
189
190
We test different object which have no appropriate answer::
191
192
sage: import_statements('my_tailor_is_rich')
193
Traceback (most recent call last):
194
...
195
ValueError: no import statement for my_tailor_is_rich
196
sage: import_statements(5)
197
Traceback (most recent call last):
198
...
199
ValueError: no import statement for 5
200
201
We test that it behaves well with lazy imported objects (:trac:`14767`)::
202
203
sage: import_statements(NN)
204
from sage.rings.semirings.non_negative_integer_semiring import NN
205
sage: import_statements('NN')
206
from sage.rings.semirings.non_negative_integer_semiring import NN
207
"""
208
import inspect, sys, re
209
import sage.all
210
from sage.misc import sageinspect
211
from sage.misc.flatten import flatten
212
from sage.misc.lazy_import import LazyImport
213
214
lazy = options.get("lazy", False)
215
verbose = options.get("verbose", True)
216
if lazy:
217
print "from sage.misc.lazy_import import lazy_import"
218
219
for obj in objects:
220
# if obj is a string use it has a name and look for an object
221
if isinstance(obj, str):
222
name = obj
223
if name in sage.all.__dict__:
224
obj = sage.all.__dict__[name]
225
else:
226
# Look for the first module which contains that name.
227
# TODO: improve this heuristic.
228
for module in sys.modules.values():
229
if hasattr(module, '__dict__') and name in module.__dict__:
230
obj = module.__dict__[name]
231
break
232
else:
233
name = None
234
235
if isinstance(obj, LazyImport):
236
obj = obj._get_object()
237
238
# Case 1: the object is a module
239
if inspect.ismodule(obj):
240
if lazy:
241
print "lazy_import('%s')"%obj.__name__
242
else:
243
print "import %s"%obj.__name__
244
continue
245
246
# Case 2: the object is defined in its module
247
module = None
248
if sageinspect.isclassinstance(obj):
249
module = obj.__class__.__module__
250
elif hasattr(obj, '__module__') and obj.__module__:
251
module = obj.__module__
252
253
if module:
254
d = sys.modules[module].__dict__
255
if name is None:
256
names = sorted(key for key in d if d[key] is obj)
257
else:
258
names = [name]
259
if names:
260
if verbose and len(names) > 1:
261
print " ** Warning **: several names for that object: %s"%', '.join(names)
262
print_import_statement(module, names[0], lazy)
263
continue
264
265
266
# Case 3: search for this object in all modules
267
names = {} # dictionnary: module -> names of the object in that module
268
for module in sys.modules:
269
if module != '__main__' and hasattr(sys.modules[module],'__dict__'):
270
d = sys.modules[module].__dict__
271
272
if name is not None:
273
if name in d and d[name] is obj:
274
names[module] = name
275
else:
276
n = [key for key in d if d[key] is obj]
277
if n:
278
names[module] = n
279
280
all_names = sorted(set(flatten(names.values())))
281
if len(all_names) == 0:
282
raise ValueError("no import statement for %s"%obj)
283
elif verbose and len(all_names) > 1:
284
print " ** Warning **: several names for that object:",
285
print ", ".join(sorted(all_names))
286
287
modules = sorted(flatten(names),cmp=module_names_cmp)
288
if verbose and len(modules) > 1:
289
print " ** Warning **: several modules for that object:",
290
print ", ".join(modules[:4]),
291
if len(modules) > 4:
292
print "..."
293
else:
294
print
295
296
# Case 4: if the object is a class instance, we look for a
297
# module where it is instanciated
298
if sageinspect.isclassinstance(obj):
299
names_pattern = dict((name,re.compile("^%s\ *="%name, re.MULTILINE)) for name in all_names)
300
301
for module in modules:
302
sources = sageinspect.sage_getsource(sys.modules[module])
303
for name in names[module]:
304
if names_pattern[name].search(sources):
305
break
306
else:
307
continue
308
break
309
else:
310
module = modules[0]
311
name = names[module][0]
312
313
if name is not None:
314
print_import_statement(module, name, lazy)
315
else:
316
raise ValueError("no import statement for %s"%obj)
317
318