Modify

Opened 15 years ago

Last modified 3 years ago

#4904 new enhancement

Render documentation for modules packaged in EGG | ZIP files | PEP-302 compatible sources

Reported by: Olemis Lang Owned by: Alec Thomas
Priority: high Component: PyDocPlugin
Severity: major Keywords: PEP 302, egg, plugins, pydoc
Cc: Trac Release: 0.11

Description

I implemented this enhancement when I was setting up the Trac environment I use to manage the development of many plugins (especially TracGViz plugin which is meant to embed iGoogle gadgets in wiki pages using WikiFormatting). I'm used to see in action in the same site the plugins I build (yes, I eat my own dog food ;). That time I realized that even when TracGViz plugin was installed and living in sys.path, its documentation wasnt shown, and its path was not included in the index page.

I discovered that this was due to two main reasons:

  • For the index page, PyDocPlugin filters sys.path explicitly so that only the plugins found in the file system be considered.
  • For the module documentation, the fact is that the implementation relies on imp module to load the modules the documentation will be extracted from. This feature is kind of deprecated after PEP 302 -- New Import Hooks has been aproved.

Therefore I wrote a patch to solve the second issue. You can see it in action to generate the API reference documentation for TracGViz plugin.

  • .py

    old new  
    169169
    170170    def load_object(self, fullobject):
    171171        """ Load an arbitrary object from a full dotted path. """
     172        self.log.debug("Preparing docs for %s", fullobject)
    172173        fullspec = fullobject.split('.')
    173174        i = 0
    174175        module = mfile = mdescr = None
    175176        mpath = self.syspath
     177        self.log.debug("Using PATH = %s", mpath)
    176178        # Find module
     179        import sys
    177180        if fullspec[0] in sys.builtin_module_names:
    178181            module = __import__(fullspec[0], None, None)
    179182            i += 1
    180183        else:
    181             while i < len(fullspec):
     184
     185#            I removed this since it is not compatible with PEP 302
     186#            import hooks and it doesn't load the modules packaged
     187#            in zip | egg files.
     188
     189#            while i < len(fullspec):
     190#                try:
     191#                    f, p, mdescr = imp.find_module(fullspec[i], mpath)
     192#                    if mfile:
     193#                        mfile.close()
     194#                    mfile, mpath = f, [p]
     195#                    i += 1
     196#                except ImportError:
     197#                    break
     198            mname = fullspec[0]
     199            import sys              # Ensure loaders cache is updated
     200           
     201            def importer(path):
    182202                try:
    183                     f, p, mdescr = imp.find_module(fullspec[i], mpath)
    184                     if mfile:
    185                         mfile.close()
    186                     mfile, mpath = f, [p]
    187                     i += 1
    188                 except ImportError:
     203                    return sys.path_importer_cache[path]
     204                except KeyError: # Not in cache. Try path hooks instead
     205                    for imp_type in sys.path_hooks:
     206                        try:
     207                            return imp_type(path)
     208                        except:
     209                            pass
     210                    else:        # No further choices.
     211                        return imp.NullImporter(path)
     212            for path in mpath:
     213                i = 0
     214                _imp = importer(path)
     215                if isinstance(_imp, imp.NullImporter):
     216                        continue
     217                if _imp is None:  # Legacy code. No importer object :(
     218                    mpath = [path]
     219                    while i < len(fullspec):
     220                        try:
     221                            f, p, mdescr = imp.find_module(fullspec[i], mpath)
     222                            if mfile:
     223                                mfile.close()
     224                            mfile, mpath = f, [p]
     225                            i += 1
     226                        except ImportError:
     227                            break
     228                else:
     229                    mname = ''
     230                    while i < len(fullspec):
     231                        try:
     232                            mname+= fullspec[i]
     233                            self.log.debug("Trying importer '%s' for object '%s'",
     234                                    _imp, mname)
     235                            if _imp.find_module(mname) is None:
     236                                break
     237                            self.log.debug("Ok")
     238                            path = os.path.join(path, fullspec[i])
     239                            i += 1
     240                            self.log.debug("Setting new path %s", path)
     241                            _imp = importer(path)
     242                            self.log.debug("New importer %s", _imp)
     243                            mname+= '.'
     244                        except Exception, exc:
     245                            self.log.debug("Error %s message '%s'", \
     246                                            exc.__class__,
     247                                            str(exc))
     248                self.log.debug("Search for '%s' stopped at %d",
     249                            fullobject, i)
     250                if i > 0:
    189251                    break
     252            else:
     253                self.log.debug("Unable to import '%s'", fullobject)
     254                raise ImportError, fullobject
    190255            try:
    191256                mname = ".".join(fullspec[0:i])
    192                 if sys.modules.has_key(mname):
    193                     module =  sys.modules[mname]
    194                 elif mname and mdescr:
    195                     module = imp.load_module(mname, mfile, mpath[0], mdescr)
     257                self.log.debug("Module name %s", mname)
     258                __import__(mname, None, None)
     259                self.log.debug("Retrieving %s from sys.path", mname)
     260                module =  sys.modules[mname]
     261                self.log.debug("Ok")
    196262            finally:
    197263                if mfile:
    198264                    mfile.close()
     
    237303
    238304    def _makedoc(self, target, visibility):
    239305        """Warning: this helper method returns a `str` object."""
     306        self.log.debug('Loading module and object')
    240307        module, object = self.load_object(target)
     308        self.log.debug('Module and object loaded')
    241309        try:
    242310            self.makedoc_lock.acquire()
    243311            if visibility == 'private' or \

The first issue still remains open, since that PEP doesnt provide standards for module enumeration.

Best Regards,

Olemis Lang

Attachments (0)

Change History (0)

Modify Ticket

Change Properties
Set your email in Preferences
Action
as new The owner will remain Alec Thomas.

Add Comment


E-mail address and name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.