#!/usr/bin/env python # system ### Twisted Preamble # This makes sure that users don't have to set up their environment # specially in order to run these programs from bin/. import sys, os, string, shutil if string.find(os.path.abspath(sys.argv[0]), os.sep+'Twisted') != -1: sys.path.insert(0, os.path.normpath(os.path.join(os.path.abspath(sys.argv[0] ), os.pardir, os.pardir))) sys.path.insert(0, os.curdir) ### end of preamble import os, sys tmp_dir = '_epyrun_tmp' #only used for partial builds # HACK: Don't import stuff that we don't like # this is why we have to convert def myimport(*args): try: return im(*args) except: mod = FakeModule(args[0], 4) i = args[0].rfind('.') if i != -1: setattr(sys.modules[args[0][:i]], args[0][i+1:], mod) return mod class FakeModule: def __init__(self, name, level): self.__level = level self.__name__ = name def __repr__(self): return '' % self.__name__ __str__ = __repr__ def __nonzero__(self): return 1 def __call__(self, *args, **kw): pass #print 'Called:', args def __getattr__(self, attr): if self.__level == 0: raise AttributeError return FakeModule(self.__name__+'.'+attr, self.__level-1) def __cmp__(self, other): if not hasattr(other, '___name__'): return -1 return cmp(self.__name__, other.__name__) def fakeOut(modname): modpath = modname.split('.') prevmod = None for m in range(len(modpath)): mp = '.'.join(modpath[:m+1]) nm = FakeModule(mp, 4) if prevmod: setattr(prevmod, modpath[m], nm) sys.modules[mp] = nm prevmod = nm fakeOut("gnome.ui") fakeOut("pygtk") fakeOut("gtk") fakeOut("wxPython.wx") # HACK: make Twisted Interfaces not use __metaclass__ = # MetaInterface. Epydoc barfs on any classes whose type isn't # TypeType. from twisted.python import components class Interface: pass Interface.__module__ = 'twisted.python.components' Interface.__doc__ = components.Interface.__doc__ components.Interface = Interface # initial twisted from twisted.python import reflect from twisted.internet import reactor # epydoc import epydoc assert epydoc.__version__[0] == '2', "You need epydoc 2.x!" from epydoc.cli import cli from epydoc.html import HTMLFormatter from epydoc import uid import epydoc.html # HACK: Force everything to be public. def is_private(self): return False def is_public(self): return True uid.UID.is_private = is_private uid.UID.is_public = is_public # HACK: Don't append -module and -class to the filenames, and generate # redirecty-files for all methods. class TwistedHTMLFormatter(HTMLFormatter): def _uid_to_filename(self, uid): # Enforce same restrictions as HTMLFormatter._uid_to_filename for sanity # checking assert uid.is_module() or uid.is_class(), 'Bad UID type: %r' % (uid,) return uid.name() + '.html' def _uid_to_uri(self, uid): if uid.is_module() or uid.is_class(): return uid.name() + '.html' parent = uid.parent() if parent is None: return uid.name() + '.html' return parent.name() + '.html#' + uid.shortname() def _method_to_html(self, uid): """ Dodgy redirect hack. """ str = ('\n' '\n' '\n') str = str % (self._uid_to_uri(uid.parent()), uid.shortname()) return str def write(self, directory=None, progress_callback=None): HTMLFormatter.write(self, directory, progress_callback) # Write method redirectors self._write_method_redirects(directory) def _write_method_redirects(self, directory): import os.path seen = {} for uid, doc in self._docmap.data.iteritems(): if uid.is_method() or uid.is_function(): if uid.name() in seen: continue seen[uid.name()] = 1 filename = os.path.join(directory, uid.name() + '.html') #assert not os.path.exists(filename), filename s = self._method_to_html(uid) open(filename, 'w').write(s) # when doing non-full builds we need to fake epydoc in to thinking that # all twisted modules are being documented so that the html links # to the modules that aren't being documented get generated correctly def _documented(self, uid): value = HTMLFormatter._documented(self, uid) if not document_all: if not value: try: if uid._name.startswith('twisted.'): #ha ha sucker return True except: pass return value epydoc.html.HTMLFormatter = TwistedHTMLFormatter # HACK: Only document stuff that we _tell_ you to document, you stupid # &#@&!#@ from epydoc import objdoc orig_add = objdoc.DocMap._add def _add(self, objID): m = objID.module() if m and m.name() not in modnames: # see below for where modnames is defined return return orig_add(self, objID) objdoc.DocMap._add = _add # HACK: Another "only doc what we tell you". We don't want epydoc to # automatically recurse into subdirectories: "twisted"'s presence was # causing "twisted/test" to be docced, even thought we explicitly # didn't put any twisted/test in our modnames. from epydoc import imports orig_find_modules = imports.find_modules import re def find_modules(dirname): if not os.path.isdir(dirname): return [] found_init = 0 modules = {} dirs = [] # Search for directories & modules, and check for __init__.py. # Don't include duplicates (like foo.py and foo.pyc), and give # precedance to the .py files. for file in os.listdir(dirname): filepath = os.path.join(dirname, file) if os.path.isdir(filepath): dirs.append(filepath) elif not re.match(r'\w+.py.?', file): continue # Ignore things like ".#foo.py" or "a-b.py" elif file[-3:] == '.py': modules[file] = os.path.join(dirname, file) if file == '__init__.py': found_init = 1 elif file[-4:-1] == '.py': modules.setdefault(file[:-1], file) if file[:-1] == '__init__.py': found_init = 1 modules = modules.values() # If there was no __init__.py, then this isn't a package # directory; return nothing. if not found_init: return [] # Recurse to the child directories. # **twisted** here's the change: commented next line out #for d in dirs: modules += find_modules(d) return modules imports.find_modules = find_modules # Now, set up the list of modules for epydoc to document modnames = [] def addMod(arg, path, files): for fn in files: file = os.path.join(path, fn).replace('%s__init__'%os.sep, '') if file[-3:] == '.py' and not file.count('%stest%s' % (os.sep,os.sep)): modName = file[:-3].replace(os.sep,'.') try: #print 'pre-loading', modName reflect.namedModule(modName) except ImportError: print 'import error:', modName except: print 'other error:', modName else: modnames.append(modName) document_all = True # are we doing a full build? names = ['twisted/'] #default, may be overriden below #get list of modules/pkgs on cmd-line try: i = sys.argv.index("--modules") except: pass else: names = sys.argv[i+1:] document_all = False sys.argv[i:] = [] #sanity check on names for i in range(len(names)): try: j = names[i].rindex('twisted/') except: raise SystemExit, 'You can only specify twisted modules or packages' else: #strip off any leading directories before the 'twisted/' #dir. this makes it easy to specify full paths, such as #from TwistedEmacs names[i] = names[i][j:] old_out_dir = "html" #if -o was specified, we need to change it to point to a tmp dir #otherwise add our own -o option try: i = sys.argv.index('-o') old_out_dir = sys.argv[i+1] try: os.mkdir(tmp_dir) except OSError: pass sys.argv[i+1] = tmp_dir except ValueError: sys.argv[1:1] = ['-o', tmp_dir] osrv = sys.argv sys.argv=["IGNORE"] im = __import__ __builtins__.__import__ = myimport for name in names: if name.endswith(".py"): # turn it in to a python module name name = name[:-3].replace(os.sep, ".") try: reflect.namedModule(name) except ImportError: print 'import error:', name except: print 'other error:', name else: modnames.append(name) else: #assume it's a dir os.path.walk(name, addMod, None) __builtins__.__import__ = im sys.argv = osrv if 'twisted.test' in modnames: modnames.remove('twisted.test') ##if 'twisted' in modnames: ## modnames.remove('twisted') sys.argv.extend(modnames) from twisted import copyright sys.argv[1:1] = ['-n', 'Twisted %s' % copyright.version, '-u', 'http://twistedmatrix.com/', '--no-private'] # Make it easy to profile epyrun if 0: import profile profile.run('cli()', 'epyrun.prof') else: cli() # used when doing partial builds to move the new files # out of the tmp dir and in to the real output dir. # only does "twisted." files since the others (index.html and such) # won't be right when not doing full builds. def moveFilesOut(arg, dirname, fnames): for fn in fnames: if fn.startswith('twisted.'): shutil.move('%s%s%s' % (tmp_dir, os.sep, fn), old_out_dir) if not document_all: print "Updating files in %s" % old_out_dir #move the right html files in to place os.path.walk(tmp_dir, moveFilesOut, None) #clean up shutil.rmtree(tmp_dir) print 'Done!'