示例#1
0
 def can_document_member(cls, member, membername, isattr, parent):
     # It can be documented if it is a genuine function.
     # Often, a class instance has the same documentation as its class,
     # and then we typically want to document the class and not the instance.
     # However, there is an exception: CachedFunction(f) returns a class instance,
     # whose doc string coincides with that of f and is thus different from
     # that of the class CachedFunction. In that situation, we want that f is documented.
     # This is part of SAGE TRAC 9976
     return isinstance(member, (FunctionType, BuiltinFunctionType)) or (isclassinstance(member) and _sage_getdoc_unformatted(member)!=_sage_getdoc_unformatted(member.__class__))
示例#2
0
 def args_on_obj(obj):
     if hasattr(obj, "_sage_argspec_"):
         return obj._sage_argspec_()
     if inspect.isbuiltin(obj) or \
            inspect.ismethoddescriptor(obj):
         # can never get arguments of a C function or method unless
         # a function to do so is supplied
         if self.env.config.autodoc_builtin_argspec:
             argspec = self.env.config.autodoc_builtin_argspec(obj)
             return argspec
         else:
             return None
     argspec = sage_getargspec(obj) #inspect.getargspec(obj)
     if isclassinstance(obj) or inspect.isclass(obj):
         # if a class should be documented as function, we try
         # to use the constructor signature as function
         # signature without the first argument.
         if argspec is not None and argspec[0]:
             del argspec[0][0]
     return argspec
示例#3
0
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.rings.arith 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 dectect it::

        sage: import_statements('FareySymbol')
        from sage.modular.arithgroup.farey_symbol import Farey as FareySymbol

        sage: import_statements('sum')
        from sage.misc.functional import symbolic_sum as sum

        sage: import_statements('power')
        from sage.structure.element 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('EnumeratedSetFromIterator')
        Traceback (most recent call last):
        ...
        LookupError: no object named 'EnumeratedSetFromIterator'
        sage: from sage.misc.dev_tools import load_submodules
        sage: load_submodules(sage.sets)
        load sage.sets.cartesian_product... succeeded
        load sage.sets.set_from_iterator... succeeded
        sage: import_statements('EnumeratedSetFromIterator')
        from sage.sets.set_from_iterator import EnumeratedSetFromIterator

    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

    .. 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.
    """
    import inspect
    from sage.misc.lazy_import import LazyImport

    answer = {}  # a dictionnary module -> [(name1,alias1), (name2,alias2) ...]
    # where "nameX" is an object in "module" that has to be
    # imported with the alias "aliasX"

    lazy = kwds.pop("lazy", False)
    verbose = kwds.pop("verbose", True)
    answer_as_str = kwds.pop("answer_as_str", False)

    if kwds:
        raise TypeError("Unexpected '%s' argument" % kwds.keys()[0])

    for obj in objects:
        name = None  # the name of the object

        # 1. if obj is a string, we look for an object that has that name
        if isinstance(obj, str):
            name = obj
            obj = find_objects_from_name(name, 'sage')
            if len(obj) == 0:
                obj = find_objects_from_name(name)

            # remove lazy imported objects from list obj
            i = 0
            deprecation = None
            while i < len(obj):
                if isinstance(obj[i], LazyImport):
                    tmp = obj.pop(i)
                    # Ignore deprecated lazy imports
                    tmp_deprecation = tmp._get_deprecation_ticket()
                    if tmp_deprecation:
                        deprecation = tmp_deprecation
                    else:
                        tmp = tmp._get_object()
                        if all(u is not tmp for u in obj):
                            obj.append(tmp)
                else:
                    i += 1

            if verbose and len(obj) > 1:
                modules = set()
                for o in obj:
                    modules.update(find_object_modules(o))
                print("# **Warning**: distinct objects with name '{}' in:".
                      format(name))
                for module_name in modules:
                    print("#   - {}".format(module_name))

            # choose a random object among the potentially enormous list of
            # objects we get from "name"
            try:
                obj = obj[0]
            except IndexError:
                if deprecation:
                    raise LookupError(
                        "object named %r is deprecated (see trac ticket %s)" %
                        (name, deprecation))
                else:
                    raise LookupError("no object named %r" % name)

        # 1'. if obj is a LazyImport we recover the real object
        if isinstance(obj, LazyImport):
            obj = obj._get_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
        if inspect.ismodule(obj):
            module_name = obj.__name__
            if module_name not in answer:
                answer[module_name] = []
            answer[module_name].append((None, None))
            continue

        modules = find_object_modules(obj)
        if '__main__' in modules:
            del modules['__main__']

        if not modules:
            raise ValueError("no import statement found for '{}'.".format(obj))

        if len(modules) == 1:  # the module is well defined
            module_name, obj_names = modules.items()[0]
            if name is None:
                if verbose and len(obj_names) > 1:
                    print("# ** Warning **: several names for that object: {}".
                          format(', '.join(sorted(obj_names))))
                name = alias = obj_names[0]
            elif name in modules[module_name]:
                alias = name
            else:
                alias = name
                name = obj_names[0]

            if module_name not in answer:
                answer[module_name] = []

            answer[module_name].append((name, alias))
            continue

        # 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 name is not None:
            good_modules = []
            for module_name in modules:
                if name in modules[module_name]:
                    good_modules.append(module_name)

            if len(good_modules) == 1:
                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
        from sageinspect import isclassinstance
        if isclassinstance(obj):
            module_name = type(obj).__module__
            i = module_name.rfind('.')
            all_module_name = module_name[:i] + '.all'
            if all_module_name in modules:
                module_name = all_module_name
                modules[module_name][0]
            else:
                module_name = None

        if module_name is 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
                not '.all_' 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 = modules.keys()[0]
            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
        if name is None:
            alias = name = modules[module_name][0]
        else:
            alias = name
            name = modules[module_name][0]

        if module_name not in answer:
            answer[module_name] = []
        answer[module_name].append((name, alias))

    res = []

    if lazy:
        res.append("from sage.misc.lazy_import import lazy_import")

    for module_name in sorted(answer.keys()):
        res.append(
            import_statement_string(module_name, answer[module_name], lazy))

    if answer_as_str:
        return '\n'.join(res)
    else:
        print('\n'.join(res))
示例#4
0
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.rings.arith 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 dectect it::

        sage: import_statements('FareySymbol')
        from sage.modular.arithgroup.farey_symbol import Farey as FareySymbol

        sage: import_statements('sum')
        from sage.misc.functional import symbolic_sum as sum

        sage: import_statements('power')
        from sage.structure.element 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('EnumeratedSetFromIterator')
        Traceback (most recent call last):
        ...
        LookupError: no object named 'EnumeratedSetFromIterator'
        sage: from sage.misc.dev_tools import load_submodules
        sage: load_submodules(sage.sets)
        load sage.sets.cartesian_product... succeeded
        load sage.sets.set_from_iterator... succeeded
        sage: import_statements('EnumeratedSetFromIterator')
        from sage.sets.set_from_iterator import EnumeratedSetFromIterator

    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

    .. 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.
    """
    import inspect
    from sage.misc.lazy_import import LazyImport

    answer = {}   # a dictionnary module -> [(name1,alias1), (name2,alias2) ...]
                  # where "nameX" is an object in "module" that has to be
                  # imported with the alias "aliasX"

    lazy = kwds.pop("lazy", False)
    verbose = kwds.pop("verbose", True)
    answer_as_str = kwds.pop("answer_as_str", False)

    if kwds:
        raise TypeError("Unexpected '%s' argument"%kwds.keys()[0])

    for obj in objects:
        name = None    # the name of the object

        # 1. if obj is a string, we look for an object that has that name
        if isinstance(obj, str):
            name = obj
            obj = find_objects_from_name(name, 'sage')
            if len(obj) == 0:
                obj = find_objects_from_name(name)

            # remove lazy imported objects from list obj
            i = 0
            deprecation = None
            while i < len(obj):
                if isinstance(obj[i], LazyImport):
                    tmp = obj.pop(i)
                    # Ignore deprecated lazy imports
                    tmp_deprecation = tmp._get_deprecation_ticket()
                    if tmp_deprecation:
                        deprecation = tmp_deprecation
                    else:
                        tmp = tmp._get_object()
                        if all(u is not tmp for u in obj):
                            obj.append(tmp)
                else:
                    i += 1

            if verbose and len(obj) > 1:
                modules = set()
                for o in obj:
                    modules.update(find_object_modules(o))
                print("# **Warning**: distinct objects with name '{}' in:".format(name))
                for module_name in modules:
                    print("#   - {}".format(module_name))

            # choose a random object among the potentially enormous list of
            # objects we get from "name"
            try:
                obj = obj[0]
            except IndexError:
                if deprecation:
                    raise LookupError("object named %r is deprecated (see trac ticket %s)"%(name, deprecation))
                else:
                    raise LookupError("no object named %r"%name)

        # 1'. if obj is a LazyImport we recover the real object
        if isinstance(obj, LazyImport):
            obj = obj._get_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
        if inspect.ismodule(obj):
            module_name = obj.__name__
            if module_name not in answer:
                answer[module_name] = []
            answer[module_name].append((None,None))
            continue

        modules = find_object_modules(obj)
        if '__main__' in modules:
            del modules['__main__']

        if not modules:
            raise ValueError("no import statement found for '{}'.".format(obj))

        if len(modules) == 1:  # the module is well defined
            module_name, obj_names = modules.items()[0]
            if name is None:
                if verbose and len(obj_names) > 1:
                    print("# ** Warning **: several names for that object: {}".format(', '.join(sorted(obj_names))))
                name = alias = obj_names[0]
            elif name in modules[module_name]:
                alias = name
            else:
                alias = name
                name = obj_names[0]

            if module_name not in answer:
                answer[module_name] = []

            answer[module_name].append((name,alias))
            continue

        # 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 name is not None:
            good_modules = []
            for module_name in modules:
                if name in modules[module_name]:
                    good_modules.append(module_name)

            if len(good_modules) == 1:
                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
        from sageinspect import isclassinstance
        if isclassinstance(obj):
            module_name = type(obj).__module__
            i = module_name.rfind('.')
            all_module_name = module_name[:i] + '.all'
            if all_module_name in modules:
                module_name = all_module_name
                modules[module_name][0]
            else:
                module_name = None

        if module_name is 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 not '.all_' 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 = modules.keys()[0]
            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
        if name is None:
            alias = name = modules[module_name][0]
        else:
            alias = name
            name = modules[module_name][0]

        if module_name not in answer:
            answer[module_name] = []
        answer[module_name].append((name,alias))

    res = []

    if lazy:
        res.append("from sage.misc.lazy_import import lazy_import")

    for module_name in sorted(answer.keys()):
        res.append(import_statement_string(module_name, answer[module_name], lazy))

    if answer_as_str:
        return '\n'.join(res)
    else:
        print('\n'.join(res))