diff --git a/etc/inst.diff b/converter/etc/inst.diff similarity index 100% rename from etc/inst.diff rename to converter/etc/inst.diff diff --git a/ez_setup.py b/ez_setup.py new file mode 100644 index 000000000..199a17f64 --- /dev/null +++ b/ez_setup.py @@ -0,0 +1,223 @@ +#!python +"""Bootstrap setuptools installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import sys +DEFAULT_VERSION = "0.6c5" +DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3] + +md5_data = { + 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', + 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', + 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', + 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', + 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', + 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', + 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', + 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', + 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', + 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', + 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', + 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', + 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', + 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', + 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', + 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', + 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', + 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', + 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', + 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', + 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', +} + +import sys, os + +def _validate_md5(egg_name, data): + if egg_name in md5_data: + from md5 import md5 + digest = md5(data).hexdigest() + if digest != md5_data[egg_name]: + print >>sys.stderr, ( + "md5 validation of %s failed! (Possible download problem?)" + % egg_name + ) + sys.exit(2) + return data + + +def use_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + download_delay=15 +): + """Automatically find/download setuptools and make it available on sys.path + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end with + a '/'). `to_dir` is the directory where setuptools will be downloaded, if + it is not already available. If `download_delay` is specified, it should + be the number of seconds that will be paused before initiating a download, + should one be required. If an older version of setuptools is installed, + this routine will print a message to ``sys.stderr`` and raise SystemExit in + an attempt to abort the calling script. + """ + try: + import setuptools + if setuptools.__version__ == '0.0.1': + print >>sys.stderr, ( + "You have an obsolete version of setuptools installed. Please\n" + "remove it from your system entirely before rerunning this script." + ) + sys.exit(2) + except ImportError: + egg = download_setuptools(version, download_base, to_dir, download_delay) + sys.path.insert(0, egg) + import setuptools; setuptools.bootstrap_install_from = egg + + import pkg_resources + try: + pkg_resources.require("setuptools>="+version) + + except pkg_resources.VersionConflict, e: + # XXX could we install in a subprocess here? + print >>sys.stderr, ( + "The required version of setuptools (>=%s) is not available, and\n" + "can't be installed while this script is running. Please install\n" + " a more recent version first.\n\n(Currently using %r)" + ) % (version, e.args[0]) + sys.exit(2) + +def download_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + delay = 15 +): + """Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download attempt. + """ + import urllib2, shutil + egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) + url = download_base + egg_name + saveto = os.path.join(to_dir, egg_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + from distutils import log + if delay: + log.warn(""" +--------------------------------------------------------------------------- +This script requires setuptools version %s to run (even to display +help). I will attempt to download it for you (from +%s), but +you may need to enable firewall access for this script first. +I will start the download in %d seconds. + +(Note: if this machine does not have network access, please obtain the file + + %s + +and place it in this directory before rerunning this script.) +---------------------------------------------------------------------------""", + version, download_base, delay, url + ); from time import sleep; sleep(delay) + log.warn("Downloading %s", url) + src = urllib2.urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = _validate_md5(egg_name, src.read()) + dst = open(saveto,"wb"); dst.write(data) + finally: + if src: src.close() + if dst: dst.close() + return os.path.realpath(saveto) + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + + try: + import setuptools + except ImportError: + egg = None + try: + egg = download_setuptools(version, delay=0) + sys.path.insert(0,egg) + from setuptools.command.easy_install import main + return main(list(argv)+[egg]) # we're done here + finally: + if egg and os.path.exists(egg): + os.unlink(egg) + else: + if setuptools.__version__ == '0.0.1': + # tell the user to uninstall obsolete version + use_setuptools(version) + + req = "setuptools>="+version + import pkg_resources + try: + pkg_resources.require(req) + except pkg_resources.VersionConflict: + try: + from setuptools.command.easy_install import main + except ImportError: + from easy_install import main + main(list(argv)+[download_setuptools(delay=0)]) + sys.exit(0) # try to force an exit + else: + if argv: + from setuptools.command.easy_install import main + main(argv) + else: + print "Setuptools version",version,"or greater has been installed." + print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' + + + +def update_md5(filenames): + """Update our built-in md5 registry""" + + import re + from md5 import md5 + + for name in filenames: + base = os.path.basename(name) + f = open(name,'rb') + md5_data[base] = md5(f.read()).hexdigest() + f.close() + + data = [" %r: %r,\n" % it for it in md5_data.items()] + data.sort() + repl = "".join(data) + + import inspect + srcfile = inspect.getsourcefile(sys.modules[__name__]) + f = open(srcfile, 'rb'); src = f.read(); f.close() + + match = re.search("\nmd5_data = {\n([^}]+)}", src) + if not match: + print >>sys.stderr, "Internal error!" + sys.exit(2) + + src = src[:match.start(1)] + repl + src[match.end(1):] + f = open(srcfile,'w') + f.write(src) + f.close() + + +if __name__=='__main__': + if len(sys.argv)>2 and sys.argv[1]=='--md5update': + update_md5(sys.argv[2:]) + else: + main(sys.argv[1:]) diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..a36928486 --- /dev/null +++ b/setup.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +import ez_setup +ez_setup.use_setuptools() +import sphinx + +from setuptools import setup, Feature + +setup( + name='Sphinx', + version=sphinx.__version__, +# url='', +# download_url='', + license='BSD', + author='Georg Brandl', + author_email='georg@python.org', + description='Python documentation generator', + long_description='', + zip_safe=False, + classifiers=[ + 'Development Status :: 2 - Pre-Alpha', + 'Environment :: Console', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Documentation', + 'Topic :: Utilities', + ], + platforms='any', + packages=['sphinx'], + include_package_data=True, + scripts=['sphinx-build.py', 'sphinx-web.py', 'sphinx-quickstart.py'], + entry_points={ + 'console_scripts': [ + 'sphinx-build = sphinx:main', + 'sphinx-web = sphinx.web:main', + 'sphinx-quickstart = sphinx.quickstart:main' + ] + }, + install_requires=['Pygments>=0.8', 'docutils==0.4'] +) diff --git a/sphinx-quickstart.py b/sphinx-quickstart.py new file mode 100644 index 000000000..cce87ab63 --- /dev/null +++ b/sphinx-quickstart.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" + Sphinx - Python documentation toolchain + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: 2008 by Georg Brandl. + :license: BSD. +""" + +import sys + +if __name__ == '__main__': + from sphinx.quickstart import main + sys.exit(main(sys.argv)) diff --git a/sphinx/__init__.py b/sphinx/__init__.py index b84decb40..fbeeca098 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -42,7 +42,7 @@ modi: * with filenames, write these.""" % (argv[0],) -def main(argv): +def main(argv=sys.argv): if not sys.stdout.isatty() or sys.platform == 'win32': # Windows' poor cmd box doesn't understand ANSI sequences nocolor() diff --git a/sphinx/application.py b/sphinx/application.py index b6676eb53..76fbe53e1 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -17,8 +17,10 @@ import sys from docutils import nodes from docutils.parsers.rst import directives, roles +import sphinx from sphinx.config import Config from sphinx.builder import builtin_builders +from sphinx.util.console import bold class ExtensionError(Exception): @@ -62,6 +64,7 @@ class Application(object): self._status = status self._warning = warning + self._warncount = 0 # read config self.config = Config(srcdir, 'conf.py') @@ -84,11 +87,14 @@ class Application(object): print >>warning, 'Builder name %s not registered' % buildername return + self.info(bold('Sphinx v%s, building %s' % (sphinx.__version__, buildername))) + builderclass = self.builderclasses[buildername] self.builder = builderclass(self, freshenv=freshenv) self.emit('builder-inited') def warn(self, message): + self._warncount += 1 self._warning.write('WARNING: %s\n' % message) def info(self, message='', nonl=False): diff --git a/sphinx/builder.py b/sphinx/builder.py index 0500cd7b8..f609e4285 100644 --- a/sphinx/builder.py +++ b/sphinx/builder.py @@ -135,7 +135,10 @@ class Builder(object): path.join(self.doctreedir, ENV_PICKLE_FILENAME)) self.info('done') except Exception, err: - self.info('failed: %s' % err) + if type(err) is IOError and err.errno == 2: + self.info('not found') + else: + self.info('failed: %s' % err) self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config) else: self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config) @@ -212,7 +215,10 @@ class Builder(object): # finish (write style files etc.) self.info(bold('finishing... ')) self.finish() - self.info(bold('build succeeded.')) + if self.app._warncount: + self.info(bold('build succeeded, %s warnings.' % self.app._warncount)) + else: + self.info(bold('build succeeded.')) def write(self, build_docnames, updated_docnames): if build_docnames is None: # build_all @@ -234,8 +240,11 @@ class Builder(object): self.env.set_warnfunc(warnings.append) for docname in self.status_iterator(sorted(docnames), 'writing output... ', darkgreen): - doctree = self.env.get_and_resolve_doctree(docname, self) - self.write_doc(docname, doctree) + try: + doctree = self.env.get_and_resolve_doctree(docname, self) + self.write_doc(docname, doctree) + except Exception, err: + warnings.append('%s:: doctree not found!' % docname) for warning in warnings: if warning.strip(): self.warn(warning) @@ -460,7 +469,7 @@ class StandaloneHTMLBuilder(Builder): def get_outdated_docs(self): for docname in get_matching_docs( self.srcdir, self.config.source_suffix, - exclude=set(self.config.unused_files)): + exclude=set(self.config.unused_docs)): targetname = self.env.doc2path(docname, self.outdir, '.html') try: targetmtime = path.getmtime(targetname) @@ -544,7 +553,7 @@ class WebHTMLBuilder(StandaloneHTMLBuilder): def get_outdated_docs(self): for docname in get_matching_docs( self.srcdir, self.config.source_suffix, - exclude=set(self.config.unused_files)): + exclude=set(self.config.unused_docs)): targetname = self.env.doc2path(docname, self.outdir, '.fpickle') try: targetmtime = path.getmtime(targetname) diff --git a/sphinx/config.py b/sphinx/config.py index 5c40a29c6..d3c9a4b59 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -35,7 +35,7 @@ class Config(object): # general reading options master_doc = ('contents', True), source_suffix = ('.rst', True), - unused_files = ([], True), + unused_docs = ([], True), add_function_parentheses = (True, True), add_module_names = (True, True), diff --git a/sphinx/environment.py b/sphinx/environment.py index 3226f617b..b489bc142 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -303,7 +303,7 @@ class BuildEnvironment: Return (added, changed, removed) sets. """ self.found_docs = set(get_matching_docs(self.srcdir, config.source_suffix, - exclude=set(config.unused_files))) + exclude=set(config.unused_docs))) # clear all files no longer present removed = set(self.all_docs) - self.found_docs @@ -368,7 +368,7 @@ class BuildEnvironment: self.read_doc(docname, app=app) if config.master_doc not in self.all_docs: - self.warn(None, 'no master file %s found' % self.doc2path(config.master_doc)) + self.warn(None, 'master file %s not found' % self.doc2path(config.master_doc)) # --------- SINGLE FILE BUILDING ------------------------------------------- diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py new file mode 100644 index 000000000..b3643a0a2 --- /dev/null +++ b/sphinx/quickstart.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +""" + sphinx.quickstart + ~~~~~~~~~~~~~~~~~ + + Quickly setup documentation source to work with Sphinx. + + :copyright: 2008 by Georg Brandl. + :license: BSD. +""" + +import sys, os, time +from os import path + +from sphinx.util.console import darkgreen, purple, bold, red, nocolor + + +QUICKSTART_CONF = '''\ +# -*- coding: utf-8 -*- +# +# %(project)s documentation build configuration file, created by +# sphinx-quickstart.py on %(now)s. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# All configuration values have a default value; values that are commented out +# show the default value as assigned to them. + +import sys + +# If your extensions are in another directory, add it here. +#sys.path.append('some/directory') + +# General configuration +# --------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.addons.*') or your custom ones. +#extensions = [] + +# Add any paths that contain templates here, relative to this directory. +#templates_path = [] + +# The suffix of source filenames. +source_suffix = '%(suffix)s' + +# The master toctree document. +master_doc = '%(master)s' + +# General substitutions. +project = %(project)r +copyright = '%(year)s, %(author)s' + +# The default replacements for |version| and |release|, also used in various +# other places throughout the built documents. +# +# The short X.Y version. +version = '%(version)s' +# The full version, including alpha/beta/rc tags. +release = '%(release)s' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +today_fmt = '%%B %%d, %%Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + + +# Options for HTML output +# ----------------------- + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +html_last_updated_fmt = '%%b %%d, %%Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Content template for the index page, filename relative to this file. +#html_index = '' + +# Custom sidebar templates, maps page names to filenames relative to this file. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# filenames relative to this file. +#html_additional_pages = {} + +# If true, the reST sources are included in the HTML build as _sources/. +#html_copy_source = True + +# Output file base name for HTML help builder. +htmlhelp_basename = '%(project)sdoc' + + +# Options for LaTeX output +# ------------------------ + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, document class [howto/manual]). +#latex_documents = [] + +# Additional stuff for the LaTeX preamble. +#latex_preamble = ' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] +''' + +def is_path(x): + """Please enter an existing path name.""" + return path.isdir(x) + +def nonempty(x): + """Please enter some text.""" + return len(x) + +def suffix(x): + """Please enter a file suffix, e.g. '.rst' or '.txt'.""" + return x[0:1] == '.' and len(x) > 1 + + +def do_prompt(d, key, text, default=None, validator=nonempty): + while True: + if default: + prompt = purple('> %s [%s]: ' % (text, default)) + else: + prompt = purple('> ' + text + ': ') + x = raw_input(prompt) + if default and not x: + x = default + if validator and not validator(x): + print red(" * " + validator.__doc__) + continue + break + d[key] = x + + +def inner_main(args): + d = {} + + if os.name == 'nt' or not sys.stdout.isatty(): + nocolor() + + print bold('Welcome to the Sphinx quickstart utility.') + print ''' +Please enter values for the following settings (just press Enter to +accept a default value, if one is given in brackets).''' + + print ''' +This tool will create "src" and "build" folders in this path, which +must be an existing directory.''' + do_prompt(d, 'path', 'Root path for the documentation', '.', is_path) + print ''' +The project name will occur in several places in the built documentation.''' + do_prompt(d, 'project', 'Project name') + do_prompt(d, 'author', 'Author name(s)') + print ''' +Sphinx has the notion of a "version" and a "release" for the +software. Each version can have multiple releases. For example, for +Python the version is something like 2.5 or 3.0, while the release is +something like 2.5.1 or 3.0a1. If you don't need this dual structure, +just set both to the same value.''' + do_prompt(d, 'version', 'Project version') + do_prompt(d, 'release', 'Project release', d['version']) + print ''' +The file name suffix for source files. Commonly, this is either ".txt" +or ".rst". Only files with this suffix are considered documents.''' + do_prompt(d, 'suffix', 'Source file suffix', '.rst', suffix) + print ''' +One document is special in that it is considered the top node of the +"contents tree", that is, it is the root of the hierarchical structure +of the documents. Normally, this is "index", but if your "index" +document is a custom template, you can also set this to another filename.''' + do_prompt(d, 'master', 'Name of your master document (without suffix)', 'index') + + d['year'] = time.strftime('%Y') + d['now'] = time.asctime() + + os.mkdir(path.join(d['path'], 'src')) + os.mkdir(path.join(d['path'], 'build')) + + f = open(path.join(d['path'], 'src', 'conf.py'), 'w') + f.write(QUICKSTART_CONF % d) + f.close() + + masterfile = path.join(d['path'], 'src', d['master'] + d['suffix']) + + print + print bold('Finished: An initial directory structure has been created.') + print ''' +You should now create your master file %s and other documentation +sources. Use the sphinx-build.py script to build the docs. +''' % (masterfile) + + +def main(argv=sys.argv): + try: + return inner_main(argv) + except (KeyboardInterrupt, EOFError): + print + print '[Interrupted.]' + return + diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index e33384f72..f96cf488f 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -63,10 +63,11 @@ def get_matching_docs(dirname, suffix, exclude=()): for sfile in files: if not fnmatch.fnmatch(sfile, pattern): continue - qualified_name = path.join(root[dirlen:], sfile) + qualified_name = path.join(root[dirlen:], sfile[:-len(suffix)]) + qualified_name = qualified_name.replace(os.path.sep, SEP) if qualified_name in exclude: continue - yield qualified_name[:-len(suffix)].replace(os.path.sep, SEP) + yield qualified_name def shorten_result(text='', keywords=[], maxlen=240, fuzz=60): diff --git a/sphinx/web/__init__.py b/sphinx/web/__init__.py index ee81f8ab3..66f0f22bb 100644 --- a/sphinx/web/__init__.py +++ b/sphinx/web/__init__.py @@ -23,7 +23,7 @@ except ImportError: DebuggedApplication = lambda x, y: x -def main(argv): +def main(argv=sys.argv): opts, args = getopt.getopt(argv[1:], "dhf:") opts = dict(opts) if len(args) != 1 or '-h' in opts: