3

If you type this:

import somemodule
help(somemodule)

it will print out paged package description. I would need to get the same description as a string but without importing this package to the current namespace. Is this possible? It surely is, because anything is possible in Python, but what is the most elegant/pythonic way of doing so?

Side note: by elegant way I mean without opening a separate process and capturing its stdout... ;)

In other words, is there a way to peek into a unimported but installed package and get its description? Maybe something with importlib.abc.InspectLoader? But I have no idea how to make it work the way I need.

UPDATE: I need not just not polluting the namespace but also do this without leaving any traces of itself or dependent modules in memory and in sys.modules etc. Like it was never really imported.

UPDATE: Before anyone asks me why I need it - I want to list all installed python packages with their description. But after this I do not want to have them imported in sys.modules nor occupying excessive space in memory because there can be a lots of them.

3 Answers3

1

The reason that you will need to import the module to get a help string is that in many cases, the help strings are actually generated in code. It would be pointlessly difficult to parse the text of such a package to get the string since you would then have to write a small Python interpreter to reconstruct the actual string.

That being said, there are ways of completely deleting a temporarily imported modules based on this answer, which summarizes a thread that appeared on the Python mailing list around 2003: http://web.archive.org/web/20080926094551/http://mail.python.org/pipermail/python-list/2003-December/241654.html. The methods described here will generally only work if the module is not referenced elsewhere. Otherwise the module will be unloaded in the sense that import will reload it from scratch instead of using the existing sys.modules entry, but the module will still live in memory.

Here is a function that does approximately what you want and even prints a warning if the module does not appear to have been unloaded. Unlike the solutions proposed in the linked answer, this function really handles all the side-effects of loading a module, including the fact that importing one package may import other external packages into sys.modules:

import sys, warnings
def get_help(module_name):
    modules_copy = sys.modules.copy()
    module = __import__(module_name)
    h = help(module)
    for modname in list(sys.modules):
        if modname not in modules_copy:
            del sys[modname]
    if sys.getrefcount(module) > 1:
        warnings.warn('Module {} is likely not to be completely wiped'.format(module_name))
    del module
    return h

The reason that I make a list of the keys in the final loop is that it is inadvisable to modify a dictionary (or any other iterable) as you iterate through it. At least in Python 3, dict.keys() returns an iterable that is backed by the dictionary itself, not a frozen copy. I am not sure if h = ... and return h are even necessary, but in the worst case, h is just None.

Community
  • 1
  • 1
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • Thanks a log, I am going to give it a try. But still it feels a bit fragile. Therefore I tend to use a separate process for importing the modules, piping back the required strings and then removing this process(es). This seems error proof and moreover I will not block my main process while acquiring the data, which I assume will take a while. – HiFile.app - best file manager Feb 10 '17 at 09:18
  • your way is indeed more reliable. You should post your own answer. Also, [this question](http://stackoverflow.com/q/42142660/2988730) came from writing the answer above. – Mad Physicist Feb 10 '17 at 10:58
  • I am going to post my answer once I get the solution working correctly. – HiFile.app - best file manager Feb 10 '17 at 13:33
0

Well, if you are only worried about keeping the global namespace tidy, you could always import in a function:

>>> def get_help():
...     import math
...     help(math)
...
>>> math
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'math' is not defined
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
-1

I would suggest a different approach, if i understand you correctly, you wish to read a portion of a package, without importing it (even within a function with local scope). I would suggest a method to do so would be via accessing the (python_path)/Lib/site-packages/(package_name)/ and reading the contents of the respective files as an alternative to importing the module so Python can.

  • This will work sometimes, but a lot of Python docstrings are generated with anything from a formatting expression to a wholly different piece of code setting the `__doc__` attribute. In those cases, you will end up re-implementing the interpreter just to get the doc-string. – Mad Physicist Feb 10 '17 at 13:37