Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" Some tools for developers
AUTHORS:
- Nicolas M. Thiery: initial version
- Vincent Delecroix (2012 and 2013): improve import_statements """ #***************************************************************************** # Copyright (C) 2011 Nicolas M. Thiery <nthiery at users.sf.net> # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #****************************************************************************** from __future__ import absolute_import from six import iteritems, string_types
def runsnake(command): """ Graphical profiling with ``runsnake``
INPUT:
- ``command`` -- the command to be run as a string.
EXAMPLES::
sage: runsnake("list(SymmetricGroup(3))") # optional - runsnake
``command`` is first preparsed (see :func:`preparse`)::
sage: runsnake('for x in range(1,4): print(x^2)') # optional - runsnake 1 4 9
:func:`runsnake` requires the program ``runsnake``. Due to non trivial dependencies (python-wxgtk, ...), installing it within the Sage distribution is unpractical. Hence, we recommend installing it with the system wide Python. On Ubuntu 10.10, this can be done with::
> sudo apt-get install python-profiler python-wxgtk2.8 python-setuptools > sudo easy_install RunSnakeRun
See the ``runsnake`` website for instructions for other platforms.
:func:`runsnake` further assumes that the system wide Python is installed in ``/usr/bin/python``.
.. SEEALSO::
- `The runsnake website <http://www.vrplumber.com/programming/runsnakerun/>`_ - ``%prun`` - :class:`Profiler`
""" import cProfile import os from sage.misc.temporary_file import tmp_filename from sage.misc.misc import get_main_globals from sage.repl.preparse import preparse tmpfile = tmp_filename() cProfile.runctx(preparse(command.lstrip().rstrip()), get_main_globals(), locals(), filename=tmpfile) os.system("/usr/bin/python -E `which runsnake` %s &" % tmpfile)
def import_statement_string(module, names, lazy): r""" Return a (lazy) import statement for ``names`` from ``module``.
INPUT:
- ``module`` -- the name of a module
- ``names`` -- a list of 2-tuples containing names and alias to import
- ``lazy`` -- a boolean: whether to return a lazy import statement
EXAMPLES::
sage: import sage.misc.dev_tools as dt sage: modname = 'sage.misc.dev_tools' sage: names_and_aliases = [('import_statement_string', 'iss')] sage: dt.import_statement_string(modname, names_and_aliases, False) 'from sage.misc.dev_tools import import_statement_string as iss' sage: dt.import_statement_string(modname, names_and_aliases, True) "lazy_import('sage.misc.dev_tools', 'import_statement_string', 'iss')" sage: dt.import_statement_string(modname, [('a','b'),('c','c'),('d','e')], False) 'from sage.misc.dev_tools import a as b, c, d as e' sage: dt.import_statement_string(modname, [(None,None)], False) 'import sage.misc.dev_tools' """ raise ValueError("can not lazy import modules") else: obj_names = "[" + ", ".join("'" + name[0] + "'" for name in names) + "]" obj_aliases = "[" + ", ".join("'" + name[1] + "'" for name in names) + "]" return "lazy_import('%s', %s, %s)" % (module, obj_names, obj_aliases) else: else:
def load_submodules(module=None, exclude_pattern=None): r""" Load all submodules of a given modules.
This method is intended to be used by developers and especially the one who uses :func:`import_statements`. By default it load the sage library and it takes around a minute.
INPUT:
- ``module`` - an optional module
- ``exclude_pattern`` - an optional regular expression pattern of module names that have to be excluded.
EXAMPLES::
sage: sage.misc.dev_tools.load_submodules(sage.combinat) load sage.combinat.algebraic_combinatorics... succeeded ... load sage.combinat.words.suffix_trees... succeeded
Calling a second time has no effect (since the function does not import modules already imported)::
sage: sage.misc.dev_tools.load_submodules(sage.combinat)
The second argument allows to exclude a pattern::
sage: sage.misc.dev_tools.load_submodules(sage.geometry, "database$|lattice") load sage.geometry.fan_isomorphism... succeeded load sage.geometry.hyperplane_arrangement.affine_subspace... succeeded ... load sage.geometry.riemannian_manifolds.surface3d_generators... succeeded
sage: sage.misc.dev_tools.load_submodules(sage.geometry) load sage.geometry.polyhedron.lattice_euclidean_group_element... succeeded load sage.geometry.polyhedron.palp_database... succeeded load sage.geometry.polyhedron.ppl_lattice_polygon... succeeded """
import sage module = sage exclude_pattern = "^sage\.libs|^sage\.tests|tests$|^sage\.all_|all$|sage\.interacts$|^sage\.misc\.benchmark$"
else:
# we exclude several sage components because loading them is much of a # problem...
except (ValueError, AttributeError, TypeError, ImportError): # we might get error because of cython code that has been # compiled but with source removed sys.stdout.write("failed\n")
def find_objects_from_name(name, module_name=None): r""" Return the list of objects from ``module_name`` whose name is ``name``.
If ``module_name`` is ``None``, the function runs through all loaded modules and returns the list of objects whose name matches ``name``.
If ``module_name`` is not ``None``, then search only in submodules of ``module_name``.
In order to search through more modules you might use the function :func:`load_submodules`.
EXAMPLES::
sage: import sage.misc.dev_tools as dt sage: dt.find_objects_from_name('FareySymbol') [<type 'sage.modular.arithgroup.farey_symbol.Farey'>]
sage: import sympy sage: dt.find_objects_from_name('RR') [Real Field with 53 bits of precision, RR] sage: dt.find_objects_from_name('RR', 'sage') [Real Field with 53 bits of precision] sage: dt.find_objects_from_name('RR', 'sympy') [RR]
Examples that do not belong to the global namespace but in a loaded module::
sage: 'find_objects_from_name' in globals() False sage: objs = dt.find_objects_from_name('find_objects_from_name') sage: len(objs) 1 sage: dt.find_objects_from_name is dt.find_objects_from_name True
.. NOTE::
It might be a good idea to move this function into :mod:`sage.misc.sageinspect`. """
def find_object_modules(obj): r""" Return a dictionary whose keys are the names of the modules where ``obj`` appear and the value at a given module name is the list of names that ``obj`` have in that module.
It is very unlikely that the output dictionary has several keys except when ``obj`` is an instance of a class.
EXAMPLES::
sage: from sage.misc.dev_tools import find_object_modules sage: find_object_modules(RR) {'sage.rings.real_mpfr': ['RR']} sage: find_object_modules(ZZ) {'sage.rings.integer_ring': ['Z', 'ZZ']}
.. NOTE::
It might be a good idea to move this function in :mod:`sage.misc.sageinspect`. """
# see if the object is defined in its own module # might be wrong for class instances as the instanciation might appear # outside of the module !!
raise ValueError("This should not happen!")
# otherwise, we parse all (already loaded) modules and hope to find # something
# if the object is an instance, we try to guess where it is defined
def import_statements(*objects, **kwds): r""" Print import statements for the given objects.
INPUT:
- ``*objects`` -- a sequence of objects or names.
- ``lazy`` -- a boolean (default: ``False``) Whether to print a lazy import statement.
- ``verbose`` -- a boolean (default: ``True``) Whether to print information in case of ambiguity.
- ``answer_as_str`` -- a boolean (default: ``False``) If ``True`` return a string instead of printing the statement.
EXAMPLES::
sage: import_statements(WeylGroup, lazy_attribute) from sage.combinat.root_system.weyl_group import WeylGroup from sage.misc.lazy_attribute import lazy_attribute
sage: import_statements(IntegerRing) from sage.rings.integer_ring import IntegerRing
If ``lazy`` is True, then :func:`lazy_import` statements are displayed instead::
sage: import_statements(WeylGroup, lazy_attribute, lazy=True) from sage.misc.lazy_import import lazy_import lazy_import('sage.combinat.root_system.weyl_group', 'WeylGroup') lazy_import('sage.misc.lazy_attribute', 'lazy_attribute')
In principle, the function should also work on object which are instances. In case of ambiguity, one or two warning lines are printed::
sage: import_statements(RDF) from sage.rings.real_double import RDF
sage: import_statements(ZZ) # ** Warning **: several names for that object: Z, ZZ from sage.rings.integer_ring import Z
sage: import_statements(euler_phi) from sage.arith.misc import euler_phi
sage: import_statements(x) from sage.calculus.predefined import x
If you don't like the warning you can disable them with the option ``verbose``::
sage: import_statements(ZZ, verbose=False) from sage.rings.integer_ring import Z
sage: import_statements(x, verbose=False) from sage.calculus.predefined import x
If the object has several names, an other way to get the import statement you expect is to use a string instead of the object::
sage: import_statements(matrix) # ** Warning **: several names for that object: Matrix, matrix from sage.matrix.constructor import Matrix
sage: import_statements('cached_function') from sage.misc.cachefunc import cached_function sage: import_statements('Z') # **Warning**: distinct objects with name 'Z' in: # - sage.calculus.predefined # - sage.rings.integer_ring from sage.rings.integer_ring import Z
Specifying a string is also useful for objects that are not imported in the Sage interpreter namespace by default. In this case, an object with that name is looked up in all the modules that have been imported in this session::
sage: import_statement_string Traceback (most recent call last): ... NameError: name 'import_statement_string' is not defined
sage: import_statements("import_statement_string") from sage.misc.dev_tools import import_statement_string
Sometimes objects are imported as an alias (from XXX import YYY as ZZZ) or are affected (XXX = YYY) and the function might detect it::
sage: import_statements('FareySymbol') from sage.modular.arithgroup.farey_symbol import Farey as FareySymbol
sage: import_statements('power') from sage.arith.power import generic_power as power
In order to be able to detect functions that belong to a non-loaded module, you might call the helper :func:`load_submodules` as in the following::
sage: import_statements('HeckeMonoid') Traceback (most recent call last): ... LookupError: no object named 'HeckeMonoid' sage: from sage.misc.dev_tools import load_submodules sage: load_submodules(sage.monoids) load sage.monoids.automatic_semigroup... succeeded load sage.monoids.hecke_monoid... succeeded load sage.monoids.indexed_free_monoid... succeeded sage: import_statements('HeckeMonoid') from sage.monoids.hecke_monoid import HeckeMonoid
We test different objects which have no appropriate answer::
sage: import_statements('my_tailor_is_rich') Traceback (most recent call last): ... LookupError: no object named 'my_tailor_is_rich' sage: import_statements(5) Traceback (most recent call last): ... ValueError: no import statement found for '5'.
We test that it behaves well with lazy imported objects (:trac:`14767`)::
sage: import_statements(NN) from sage.rings.semirings.non_negative_integer_semiring import NN sage: import_statements('NN') from sage.rings.semirings.non_negative_integer_semiring import NN
Deprecated lazy imports are ignored (see :trac:`17458`)::
sage: lazy_import('sage.all', 'RR', 'deprecated_RR', namespace=sage.__dict__, deprecation=17458) sage: import_statements('deprecated_RR') Traceback (most recent call last): ... LookupError: object named 'deprecated_RR' is deprecated (see trac ticket 17458) sage: lazy_import('sage.all', 'RR', namespace=sage.__dict__, deprecation=17458) sage: import_statements('RR') from sage.rings.real_mpfr import RR
The following were fixed with :trac:`15351`::
sage: import_statements('Rationals') from sage.rings.rational_field import RationalField as Rationals sage: import_statements(sage.combinat.partition_algebra.SetPartitionsAk) from sage.combinat.partition_algebra import SetPartitionsAk sage: import_statements(CIF) from sage.rings.all import CIF sage: import_statements(NaN) from sage.symbolic.constants import NaN sage: import_statements(pi) from sage.symbolic.constants import pi sage: import_statements('SAGE_ENV') from sage.env import SAGE_ENV sage: import_statements('graph_decompositions') import sage.graphs.graph_decompositions
Check that a name from the global namespace is properly found (see :trac:`23779`)::
sage: import_statements('log') from sage.functions.log import log
.. NOTE::
The programmers try to made this function as smart as possible. Nevertheless it is far from being perfect (for example it does not detect deprecated stuff). So, if you use it, double check the answer and report weird behaviors. """
# a dictionary module -> [(name1,alias1), (name2,alias2) ...] # where "nameX" is an object in "module" that has to be # imported with the alias "aliasX"
raise TypeError("Unexpected '%s' argument" % next(iter(kwds.keys())))
# 1. if obj is a string, we look for an object that has that name # 1.a. object in the sage namespace else: # 1.b. object inside a submodule of sage # 1.c. object from something already imported
# remove lazy imported objects from list obj # Ignore deprecated lazy imports else: else:
# choose a random object among the potentially enormous list of # objects we get from "name" else:
# 1'. if obj is a LazyImport we recover the real object
# 2. Find out in which modules obj lives # and update answer with a couple of strings "(name,alias)" where "name" is # the name of the object in the module and "alias" is the name of the # object
# easy case: the object is itself a module
del modules['__main__']
else:
# here modules contain several answers and we first try to see if there # is a best one (i.e. the object "obj" is contained in the module and # has name "name")
if module_name not in answer: answer[module_name] = [] answer[module_name].append((name, name)) continue
# if the object is a class instance, it is likely that it is defined in # some XYZ.all module else: module_name = None
# here, either "obj" is a class instance but there is no natural # candidate for its module or "obj" is not a class instance.
not_all_modules = [module_name for module_name in modules if '.all_' not in module_name and not module_name.endswith('.all')] if not(not_all_modules): print("# ** Warning **: the object {} is only defined in .all modules".format(obj)) module_name = next(iter(modules.keys())) else: if len(not_all_modules) > 1: print("# ** Warning **: several modules for the object {}: {}".format(obj, ', '.join(modules.keys()))) module_name = not_all_modules[0]
# 3. Now that we found the module, we fix the problem of the alias else:
return '\n'.join(res) else: |