[Python-checkins] r60374 - in doctools/trunk/sphinx: __init__.py addnodes.py addons addons/__init__.py addons/ifconfig.py addons/refcounting.py application.py builder.py config.py directives.py environment.py extension.py latexwriter.py refcounting.py util/__init__.py util/console.py util/smartypants.py

georg.brandl python-checkins at python.org
Sun Jan 27 21:23:25 CET 2008


Author: georg.brandl
Date: Sun Jan 27 21:23:25 2008
New Revision: 60374
Added:
 doctools/trunk/sphinx/addons/
 doctools/trunk/sphinx/addons/__init__.py
 doctools/trunk/sphinx/addons/ifconfig.py
 doctools/trunk/sphinx/addons/refcounting.py
 doctools/trunk/sphinx/application.py
 - copied, changed from r60174, doctools/trunk/sphinx/extension.py
Removed:
 doctools/trunk/sphinx/extension.py
 doctools/trunk/sphinx/refcounting.py
Modified:
 doctools/trunk/sphinx/__init__.py
 doctools/trunk/sphinx/addnodes.py
 doctools/trunk/sphinx/builder.py
 doctools/trunk/sphinx/config.py
 doctools/trunk/sphinx/directives.py
 doctools/trunk/sphinx/environment.py
 doctools/trunk/sphinx/latexwriter.py
 doctools/trunk/sphinx/util/__init__.py
 doctools/trunk/sphinx/util/console.py
 doctools/trunk/sphinx/util/smartypants.py
Log:
More refactoring:
* Move refcounting into an addon module.
* Rename the extension manager to Application and use it throughout.
* Fix some bugs found by pylint.
* Add "ifconfig" addon.
Modified: doctools/trunk/sphinx/__init__.py
==============================================================================
--- doctools/trunk/sphinx/__init__.py	(original)
+++ doctools/trunk/sphinx/__init__.py	Sun Jan 27 21:23:25 2008
@@ -15,55 +15,19 @@
 from os import path
 from cStringIO import StringIO
 
-from sphinx.config import Config, ConfigError
-from sphinx.builder import builders
-from sphinx.extension import EventManager
+from sphinx.application import Application
 from sphinx.util.console import nocolor
 
 __version__ = '$Revision: 5369 $'[11:-2]
 
 
-def init_builder(buildername, srcdirname, outdirname, doctreedir,
- confoverrides, status, warning=sys.stderr, freshenv=False):
- # read config
- config = Config(srcdirname, 'conf.py')
- if confoverrides:
- for key, val in confoverrides.items():
- setattr(config, key, val)
-
- # extensibility
- events = EventManager()
- for extension in config.extensions:
- try:
- mod = __import__(extension, None, None, ['setup'])
- except ImportError, err:
- raise ConfigError('Could not import extension %s' % module, err)
- if hasattr(mod, 'setup'):
- mod.setup(events, builders)
-
- if buildername not in builders:
- print >>warning, 'Builder name %s not registered' % buildername
- return None
-
- if buildername is None:
- print >>status, 'No builder selected, using default: html'
- buildername = 'html'
-
- builderclass = builders[buildername]
- builder = builderclass(srcdirname, outdirname, doctreedir,
- status_stream=status, warning_stream=warning,
- events=events, config=config, freshenv=freshenv)
- events.emit('builder-created', builder)
- return builder
-
-
 def usage(argv, msg=None):
 if msg:
 print >>sys.stderr, msg
 print >>sys.stderr
 print >>sys.stderr, """\
 usage: %s [options] sourcedir outdir [filenames...]"
-options: -b <builder> -- builder to use (one of %s)
+options: -b <builder> -- builder to use; default is html
 -a -- write all files; default is to only write new and changed files
 -E -- don't use a saved environment, always read all files
 -d <path> -- path for the cached environment and doctree files
@@ -75,7 +39,7 @@
 modi:
 * without -a and without filenames, write new and changed files.
 * with -a, write all files.
-* with filenames, write these.""" % (argv[0], ', '.join(builders))
+* with filenames, write these.""" % (argv[0],)
 
 
 def main(argv):
@@ -85,15 +49,15 @@
 
 try:
 opts, args = getopt.getopt(argv[1:], 'ab:d:D:NEqP')
- srcdirname = path.abspath(args[0])
- if not path.isdir(srcdirname):
+ srcdir = path.abspath(args[0])
+ if not path.isdir(srcdir):
 print >>sys.stderr, 'Error: Cannot find source directory.'
 return 1
- if not path.isfile(path.join(srcdirname, 'conf.py')):
+ if not path.isfile(path.join(srcdir, 'conf.py')):
 print >>sys.stderr, 'Error: Source directory doesn\'t contain conf.py file.'
 return 1
- outdirname = path.abspath(args[1])
- if not path.isdir(outdirname):
+ outdir = path.abspath(args[1])
+ if not path.isdir(outdir):
 print >>sys.stderr, 'Error: Cannot find output directory.'
 return 1
 except (IndexError, getopt.error):
@@ -113,7 +77,7 @@
 freshenv = use_pdb = False
 status = sys.stdout
 confoverrides = {}
- doctreedir = path.join(outdirname, '.doctrees')
+ doctreedir = path.join(outdir, '.doctrees')
 for opt, val in opts:
 if opt == '-b':
 buildername = val
@@ -139,18 +103,18 @@
 elif opt == '-P':
 use_pdb = True
 
- builder = init_builder(buildername, srcdirname, outdirname, doctreedir,
- confoverrides, status, sys.stderr, freshenv)
- if not builder:
+ app = Application(srcdir, outdir, doctreedir, buildername,
+ confoverrides, status, sys.stderr, freshenv)
+ if not app.builder:
 return 1
 
 try:
 if all_files:
- builder.build_all()
+ app.builder.build_all()
 elif filenames:
- builder.build_specific(filenames)
+ app.builder.build_specific(filenames)
 else:
- builder.build_update()
+ app.builder.build_update()
 except:
 if not use_pdb:
 raise
Modified: doctools/trunk/sphinx/addnodes.py
==============================================================================
--- doctools/trunk/sphinx/addnodes.py	(original)
+++ doctools/trunk/sphinx/addnodes.py	Sun Jan 27 21:23:25 2008
@@ -29,9 +29,6 @@
 def astext(self):
 return '[' + nodes.TextElement.astext(self) + ']'
 
-# refcount annotation
-class refcount(nodes.emphasis): pass
-
 # \versionadded, \versionchanged, \deprecated
 class versionmodified(nodes.Admonition, nodes.TextElement): pass
 
Added: doctools/trunk/sphinx/addons/__init__.py
==============================================================================
--- (empty file)
+++ doctools/trunk/sphinx/addons/__init__.py	Sun Jan 27 21:23:25 2008
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.addons
+ ~~~~~~~~~~~~~
+
+ Contains Sphinx features not activated by default.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
Added: doctools/trunk/sphinx/addons/ifconfig.py
==============================================================================
--- (empty file)
+++ doctools/trunk/sphinx/addons/ifconfig.py	Sun Jan 27 21:23:25 2008
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.addons.ifconfig
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Provides the ``ifconfig`` directive that allows to write documentation
+ that is included depending on configuration variables.
+
+ Usage::
+
+ .. ifconfig:: releaselevel in ('alpha', 'beta', 'rc')
+
+ This stuff is only included in the built docs for unstable versions.
+
+ The argument for ``ifconfig`` is a plain Python expression, evaluated in the
+ namespace of the project configuration (that is, all variables from ``conf.py``
+ are available.)
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+from docutils import nodes
+
+
+class ifconfig(nodes.Element): pass
+
+
+def ifconfig_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ node = ifconfig()
+ node['expr'] = arguments[0]
+ state.nested_parse(content, content_offset, node)
+ return [node]
+
+
+def process_ifconfig_nodes(app, doctree, docfilename):
+ ns = app.config.__dict__.copy()
+ ns['builder'] = app.builder.name
+ for node in doctree.traverse(ifconfig):
+ if not eval(node['expr'], ns):
+ node.replace_self([])
+ else:
+ node.replace_self(node.children)
+
+
+def setup(app):
+ app.add_node(ifconfig)
+ app.add_directive('ifconfig', ifconfig_directive, 1, (1, 0, 1))
+ app.connect('doctree-resolved', process_ifconfig_nodes)
Added: doctools/trunk/sphinx/addons/refcounting.py
==============================================================================
--- (empty file)
+++ doctools/trunk/sphinx/addons/refcounting.py	Sun Jan 27 21:23:25 2008
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.addons.refcounting
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Supports reference count annotations for C API functions. Based on
+ refcount.py and anno-api.py in the old Python documentation tools.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+from os import path
+from docutils import nodes
+
+from sphinx import addnodes
+
+
+# refcount annotation
+class refcount(nodes.emphasis): pass
+
+
+class RCEntry:
+ def __init__(self, name):
+ self.name = name
+ self.args = []
+ self.result_type = ''
+ self.result_refs = None
+
+
+class Refcounts(dict):
+ @classmethod
+ def fromfile(cls, filename):
+ d = cls()
+ fp = open(filename, 'r')
+ try:
+ for line in fp:
+ line = line.strip()
+ if line[:1] in ("", "#"):
+ # blank lines and comments
+ continue
+ parts = line.split(":", 4)
+ if len(parts) != 5:
+ raise ValueError("Wrong field count in %r" % line)
+ function, type, arg, refcount, comment = parts
+ # Get the entry, creating it if needed:
+ try:
+ entry = d[function]
+ except KeyError:
+ entry = d[function] = RCEntry(function)
+ if not refcount or refcount == "null":
+ refcount = None
+ else:
+ refcount = int(refcount)
+ # Update the entry with the new parameter or the result information.
+ if arg:
+ entry.args.append((arg, type, refcount))
+ else:
+ entry.result_type = type
+ entry.result_refs = refcount
+ finally:
+ fp.close()
+ return d
+
+ def add_refcount_annotations(self, app, doctree):
+ for node in doctree.traverse(addnodes.desc_content):
+ par = node.parent
+ if par['desctype'] != 'cfunction':
+ continue
+ if not par[0].has_key('names') or not par[0]['names']:
+ continue
+ entry = self.get(par[0]['names'][0])
+ if not entry:
+ continue
+ elif entry.result_type not in ("PyObject*", "PyVarObject*"):
+ continue
+ rc = 'Return value: '
+ if entry.result_refs is None:
+ rc += "Always NULL."
+ else:
+ rc += (entry.result_refs and "New" or "Borrowed") + " reference."
+ node.insert(0, refcount(rc, rc))
+
+
+def init_refcounts(app):
+ if app.config.refcount_file:
+ refcounts = Refcounts.fromfile(
+ path.join(app.srcdir, app.config.refcount_file))
+ app.connect('doctree-read', refcounts.add_refcount_annotations)
+
+
+def setup(app):
+ app.add_node(refcount)
+ app.add_config_value('refcount_file', '', True)
+ app.connect('builder-inited', init_refcounts)
Copied: doctools/trunk/sphinx/application.py (from r60174, doctools/trunk/sphinx/extension.py)
==============================================================================
--- doctools/trunk/sphinx/extension.py	(original)
+++ doctools/trunk/sphinx/application.py	Sun Jan 27 21:23:25 2008
@@ -1,60 +1,139 @@
 # -*- coding: utf-8 -*-
 """
- sphinx.extension
- ~~~~~~~~~~~~~~~~
+ sphinx.application
+ ~~~~~~~~~~~~~~~~~~
+
+ Sphinx application object.
+
+ Gracefully adapted from the TextPress system by Armin.
 
- Gracefully adapted from the TextPress event system by Armin.
 
 :copyright: 2008 by Georg Brandl, Armin Ronacher.
 :license: BSD.
 """
 
-from sphinx.config import ConfigError
+import sys
+
+from docutils import nodes
+from docutils.parsers.rst import directives, roles
+
+from sphinx.config import Config
+from sphinx.builder import builtin_builders
 
 
-def import_object(objname, source=None):
- """Import an object from a 'module.name' string."""
- try:
- module, name = objname.rsplit('.', 1)
- except ValueError, err:
- raise ConfigError('Invalid full object name %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
- try:
- return getattr(__import__(module, None, None, [name]), name)
- except ImportError, err:
- raise ConfigError('Could not import %s' % module +
- (source and ' (needed for %s)' % source or ''), err)
- except AttributeError, err:
- raise ConfigError('Could not find %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
+class ExtensionError(Exception):
+ """Raised if something's wrong with the configuration."""
+
+ def __init__(self, message, orig_exc=None):
+ self.message = message
+ self.orig_exc = orig_exc
+
+ def __repr__(self):
+ if self.orig_exc:
+ return '%s(%r, %r)' % (self.__class__.__name__,
+ self.message, self.orig_exc)
+ return '%s(%r)' % (self.__class__.__name__, self.message)
+
+ def __str__(self):
+ if self.orig_exc:
+ return '%s (exception: %s)' % (self.message, self.orig_exc)
+ return self.message
 
 
 # List of all known events. Maps name to arguments description.
 events = {
- 'builder-created' : 'builder instance',
+ 'builder-inited': 'builder instance',
 'doctree-read' : 'the doctree before being pickled',
+ 'doctree-resolved' : 'the doctree, the filename, the builder',
 }
 
-class EventManager(object):
- """
- Helper class that handles event listeners and events.
-
- This is *not* a public interface. Always use the emit_event()
- functions to access it or the connect_event() / disconnect_event()
- functions on the application.
- """
+class Application(object):
 
- def __init__(self):
+ def __init__(self, srcdir, outdir, doctreedir, buildername,
+ confoverrides, status, warning=sys.stderr, freshenv=False):
 self.next_listener_id = 0
 self._listeners = {}
+ self.builderclasses = builtin_builders.copy()
+ self.builder = None
 
- def _validate(self, event):
+ self.srcdir = srcdir
+ self.outdir = outdir
+ self.doctreedir = doctreedir
+
+ self._status = status
+ self._warning = warning
+
+ # read config
+ self.config = Config(srcdir, 'conf.py')
+ if confoverrides:
+ for key, val in confoverrides.items():
+ setattr(self.config, key, val)
+
+ # load all extension modules
+ for extension in getattr(self.config, 'extensions', ()):
+ self.setup_extension(extension)
+
+ # this must happen after loading extension modules, since they
+ # can add custom config values
+ self.config.init_defaults()
+
+ if buildername is None:
+ print >>status, 'No builder selected, using default: html'
+ buildername = 'html'
+ if buildername not in self.builderclasses:
+ print >>warning, 'Builder name %s not registered' % buildername
+ return
+
+ builderclass = self.builderclasses[buildername]
+ self.builder = builderclass(self, freshenv=freshenv)
+ self.emit('builder-inited')
+
+ def warn(self, message):
+ self._warning.write('WARNING: %s\n' % message)
+
+ def info(self, message='', nonl=False):
+ if nonl:
+ self._status.write(message)
+ else:
+ self._status.write(message + '\n')
+ self._status.flush()
+
+ # general extensibility interface
+
+ def setup_extension(self, extension):
+ """Import and setup a Sphinx extension module."""
+ try:
+ mod = __import__(extension, None, None, ['setup'])
+ except ImportError, err:
+ raise ExtensionError('Could not import extension %s' % extension, err)
+ if hasattr(mod, 'setup'):
+ mod.setup(self)
+
+ def import_object(self, objname, source=None):
+ """Import an object from a 'module.name' string."""
+ try:
+ module, name = objname.rsplit('.', 1)
+ except ValueError, err:
+ raise ExtensionError('Invalid full object name %s' % objname +
+ (source and ' (needed for %s)' % source or ''), err)
+ try:
+ return getattr(__import__(module, None, None, [name]), name)
+ except ImportError, err:
+ raise ExtensionError('Could not import %s' % module +
+ (source and ' (needed for %s)' % source or ''), err)
+ except AttributeError, err:
+ raise ExtensionError('Could not find %s' % objname +
+ (source and ' (needed for %s)' % source or ''), err)
+
+ # event interface
+
+ def _validate_event(self, event):
 event = intern(event)
 if event not in events:
- raise RuntimeError('unknown event name: %s' % event)
+ raise ExtensionError('Unknown event name: %s' % event)
 
 def connect(self, event, callback):
- self._validate(event)
+ self._validate_event(event)
 listener_id = self.next_listener_id
 if event not in self._listeners:
 self._listeners[event] = {listener_id: callback}
@@ -63,21 +142,39 @@
 self.next_listener_id += 1
 return listener_id
 
- def remove(self, listener_id):
+ def disconnect(self, listener_id):
 for event in self._listeners:
 event.pop(listener_id, None)
 
 def emit(self, event, *args):
- self._validate(event)
+ result = []
 if event in self._listeners:
- for listener_id, callback in self._listeners[event].iteritems():
- yield listener_id, callback(*args)
+ for _, callback in self._listeners[event].iteritems():
+ result.append(callback(self, *args))
+ return result
+
+ # registering addon parts
+
+ def add_builder(self, builder):
+ if not hasattr(builder, 'name'):
+ raise ExtensionError('Builder class %s has no "name" attribute' % builder)
+ if builder.name in self.builderclasses:
+ raise ExtensionError('Builder %r already exists (in module %s)' % (
+ builder.name, self.builderclasses[builder.name].__module__))
+ self.builderclasses[builder.name] = builder
+
+ def add_config_value(self, name, default, rebuild_env):
+ if name in self.config.values:
+ raise ExtensionError('Config value %r already present')
+ self.config.values[name] = (default, rebuild_env)
+
+ def add_node(self, node):
+ nodes._add_node_class_names([node.__name__])
+
+ def add_directive(self, name, cls, content, arguments):
+ cls.content = content
+ cls.arguments = arguments
+ directives.register_directive(name, cls)
 
-
-class DummyEventManager(EventManager):
- def connect(self, event, callback):
- self._validate(event)
- def remove(self, listener_id):
- pass
- def emit(self, event, *args):
- self._validate(event)
+ def add_role(self, name, role):
+ roles.register_canonical_role(name, role)
Modified: doctools/trunk/sphinx/builder.py
==============================================================================
--- doctools/trunk/sphinx/builder.py	(original)
+++ doctools/trunk/sphinx/builder.py	Sun Jan 27 21:23:25 2008
@@ -10,12 +10,10 @@
 """
 
 import os
-import sys
 import time
 import codecs
 import shutil
 import cPickle as pickle
-import cStringIO as StringIO
 from os import path
 from cgi import escape
 
@@ -23,18 +21,16 @@
 from docutils.io import StringOutput, FileOutput, DocTreeInput
 from docutils.core import publish_parts
 from docutils.utils import new_document
-from docutils.readers import doctree
 from docutils.frontend import OptionParser
+from docutils.readers.doctree import Reader as DoctreeReader
 
 from sphinx import addnodes
-from sphinx.util import (get_matching_files, attrdict, status_iterator,
- ensuredir, relative_uri, os_path, SEP)
+from sphinx.util import (get_matching_files, ensuredir, relative_uri, os_path, SEP)
 from sphinx.htmlhelp import build_hhx
-from sphinx.extension import DummyEventManager, import_object
 from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
 from sphinx.latexwriter import LaTeXWriter
 from sphinx.environment import BuildEnvironment, NoUri
-from sphinx.highlighting import pygments, highlight_block, get_stylesheet
+from sphinx.highlighting import pygments, get_stylesheet
 from sphinx.util.console import bold, purple, green
 
 # side effect: registers roles and directives
@@ -49,40 +45,26 @@
 Builds target formats from the reST sources.
 """
 
- def __init__(self, srcdirname, outdirname, doctreedirname,
- config, env=None, freshenv=False, events=None,
- status_stream=None, warning_stream=None):
- self.srcdir = srcdirname
- self.outdir = outdirname
- self.doctreedir = doctreedirname
- if not path.isdir(doctreedirname):
- os.mkdir(doctreedirname)
- self.freshenv = freshenv
-
- self.status_stream = status_stream or sys.stdout
- self.warning_stream = warning_stream or sys.stderr
+ def __init__(self, app, env=None, freshenv=False):
+ self.srcdir = app.srcdir
+ self.outdir = app.outdir
+ self.doctreedir = app.doctreedir
+ if not path.isdir(self.doctreedir):
+ os.mkdir(self.doctreedir)
+
+ self.app = app
+ self.warn = app.warn
+ self.info = app.info
+ self.config = app.config
 
- self.config = config
 # if None, this is set in load_env()
 self.env = env
-
- self.events = events or DummyEventManager()
+ self.freshenv = freshenv
 
 self.init()
 
 # helper methods
 
- def msg(self, message='', nonl=False, nobold=False):
- if not nobold: message = bold(message)
- if nonl:
- print >>self.status_stream, message,
- else:
- print >>self.status_stream, message
- self.status_stream.flush()
-
- def warn(self, message):
- print >>self.warning_stream, 'WARNING:', message
-
 def init(self):
 """Load necessary templates and perform initialization."""
 raise NotImplementedError
@@ -122,6 +104,17 @@
 """Return a list of output files that are outdated."""
 raise NotImplementedError
 
+ def status_iterator(self, iterable, summary, colorfunc):
+ l = -1
+ for item in iterable:
+ if l == -1:
+ self.info(bold(summary), nonl=1)
+ l = 0
+ self.info(colorfunc(item) + ' ', nonl=1)
+ yield item
+ if l == 0:
+ self.info()
+
 # build methods
 
 def load_env(self):
@@ -131,12 +124,12 @@
 return
 if not self.freshenv:
 try:
- self.msg('trying to load pickled env...', nonl=True)
+ self.info(bold('trying to load pickled env... '), nonl=True)
 self.env = BuildEnvironment.frompickle(
 path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.msg('done', nobold=True)
+ self.info('done')
 except Exception, err:
- self.msg('failed: %s' % err, nobold=True)
+ self.info('failed: %s' % err)
 self.env = BuildEnvironment(self.srcdir, self.doctreedir)
 else:
 self.env = BuildEnvironment(self.srcdir, self.doctreedir)
@@ -161,7 +154,7 @@
 self.load_env()
 to_build = self.get_outdated_files()
 if not to_build:
- self.msg('no target files are out of date, exiting.')
+ self.info(bold('no target files are out of date, exiting.'))
 return
 if isinstance(to_build, str):
 self.build([], to_build)
@@ -173,36 +166,32 @@
 
 def build(self, filenames, summary=None):
 if summary:
- self.msg('building [%s]:' % self.name, nonl=1)
- self.msg(summary, nobold=1)
+ self.info(bold('building [%s]: ' % self.name), nonl=1)
+ self.info(summary)
 
 updated_filenames = []
 # while reading, collect all warnings from docutils
 warnings = []
 self.env.set_warnfunc(warnings.append)
- self.msg('reading, updating environment:', nonl=1)
- iterator = self.env.update(
- self.config,
- hook=lambda doctree: self.events.emit('doctree-read', doctree))
- self.msg(iterator.next(), nonl=1, nobold=1)
- for filename in iterator:
- if not updated_filenames:
- self.msg('')
+ self.info(bold('updating environment: '), nonl=1)
+ iterator = self.env.update(self.config, self.app)
+ # the first item in the iterator is a summary message
+ self.info(iterator.next())
+ for filename in self.status_iterator(iterator, 'reading... ', purple):
 updated_filenames.append(filename)
- self.msg(purple(filename), nonl=1, nobold=1)
- self.msg()
+ # nothing further to do, the environment has already done the reading
 for warning in warnings:
 self.warn(warning)
 self.env.set_warnfunc(self.warn)
 
 if updated_filenames:
 # save the environment
- self.msg('pickling the env...', nonl=True)
+ self.info(bold('pickling the env... '), nonl=True)
 self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.msg('done', nobold=True)
+ self.info('done')
 
 # global actions
- self.msg('checking consistency...')
+ self.info(bold('checking consistency...'))
 self.env.check_consistency()
 
 # another indirection to support methods which don't build files
@@ -210,9 +199,9 @@
 self.write(filenames, updated_filenames)
 
 # finish (write style files etc.)
- self.msg('finishing...')
+ self.info(bold('finishing... '))
 self.finish()
- self.msg('done!')
+ self.info(bold('build succeeded.'))
 
 def write(self, build_filenames, updated_filenames):
 if build_filenames is None: # build_all
@@ -225,16 +214,15 @@
 filenames.add(tocfilename)
 filenames.add('contents.rst')
 
- self.msg('creating index...')
+ self.info(bold('creating index...'))
 self.env.create_index(self)
 self.prepare_writing(filenames)
 
 # write target files
 warnings = []
 self.env.set_warnfunc(warnings.append)
- self.msg('writing output...')
- for filename in status_iterator(sorted(filenames), green,
- stream=self.status_stream):
+ for filename in self.status_iterator(sorted(filenames),
+ 'writing output... ', green):
 doctree = self.env.get_and_resolve_doctree(filename, self)
 self.write_file(filename, doctree)
 for warning in warnings:
@@ -263,8 +251,8 @@
 """Load templates."""
 self.init_templates()
 if self.config.html_translator_class:
- self.translator_class = import_object(self.config.html_translator_class,
- 'html_translator_class setting')
+ self.translator_class = self.app.import_object(
+ self.config.html_translator_class, 'html_translator_class setting')
 elif self.config.html_use_smartypants:
 self.translator_class = SmartyPantsHTMLTranslator
 else:
@@ -277,7 +265,7 @@
 return publish_parts(
 doc,
 source_class=DocTreeInput,
- reader=doctree.Reader(),
+ reader=DoctreeReader(),
 writer=HTMLWriter(self),
 settings_overrides={'output_encoding': 'unicode'}
 )
@@ -316,7 +304,7 @@
 destination = StringOutput(encoding='utf-8')
 doctree.settings = self.docsettings
 
- output = self.docwriter.write(doctree, destination)
+ self.docwriter.write(doctree, destination)
 self.docwriter.assemble_parts()
 
 prev = next = None
@@ -360,14 +348,14 @@
 self.handle_page(pagename, context)
 
 def finish(self):
- self.msg('writing additional files...')
+ self.info(bold('writing additional files...'))
 
 # the global general index
 
 # the total count of lines for each index letter, used to distribute
 # the entries into two columns
 indexcounts = []
- for key, entries in self.env.index:
+ for _, entries in self.env.index:
 indexcounts.append(sum(1 + len(subitems) for _, (_, subitems) in entries))
 
 genindexcontext = dict(
@@ -434,7 +422,7 @@
 self.handle_page('index', {'indextemplate': indextemplate}, 'index.html')
 
 # copy style files
- self.msg('copying style files...')
+ self.info(bold('copying style files...'))
 styledirname = path.join(path.dirname(__file__), 'style')
 ensuredir(path.join(self.outdir, 'style'))
 for filename in os.listdir(styledirname):
@@ -519,7 +507,7 @@
 path.join(self.outdir, os_path(ctx['sourcename'])))
 
 def handle_finish(self):
- self.msg('dumping search index...')
+ self.info(bold('dumping search index...'))
 self.indexer.prune([fn[:-4] for fn in self.env.all_files])
 f = open(path.join(self.outdir, 'searchindex.json'), 'w')
 try:
@@ -576,7 +564,7 @@
 
 def handle_page(self, pagename, context, templatename='page.html'):
 context['current_page_name'] = pagename
- sidebarfile = self.confightml_sidebars.get(pagename, '')
+ sidebarfile = self.config.html_sidebars.get(pagename, '')
 if sidebarfile:
 context['customsidebar'] = path.join(self.srcdir, sidebarfile)
 outfilename = path.join(self.outdir, os_path(pagename) + '.fpickle')
@@ -603,7 +591,7 @@
 finally:
 f.close()
 
- self.msg('dumping search index...')
+ self.info(bold('dumping search index...'))
 self.indexer.prune(self.env.all_files)
 f = open(path.join(self.outdir, 'searchindex.pickle'), 'wb')
 try:
@@ -698,7 +686,7 @@
 doctree.settings.author = author
 doctree.settings.filename = sourcename
 doctree.settings.docclass = docclass
- output = docwriter.write(doctree, destination)
+ docwriter.write(doctree, destination)
 print "done"
 
 def assemble_doctree(self, indexfile, appendices):
@@ -746,7 +734,7 @@
 return largetree
 
 def finish(self):
- self.msg('copying TeX support files...')
+ self.info(bold('copying TeX support files...'))
 styledirname = path.join(path.dirname(__file__), 'texinputs')
 for filename in os.listdir(styledirname):
 if not filename.startswith('.'):
@@ -780,7 +768,7 @@
 libchanges = {}
 apichanges = []
 otherchanges = {}
- self.msg('writing summary file...')
+ self.info(bold('writing summary file...'))
 for type, filename, lineno, module, descname, content in \
 self.env.versionchanges[version]:
 ttext = self.typemap[type]
@@ -841,7 +829,7 @@
 break
 return line
 
- self.msg('copying source files...')
+ self.info(bold('copying source files...'))
 for filename in self.env.all_files:
 f = open(path.join(self.srcdir, os_path(filename)))
 lines = f.readlines()
@@ -868,7 +856,7 @@
 pass
 
 
-builders = {
+builtin_builders = {
 'html': StandaloneHTMLBuilder,
 'web': WebHTMLBuilder,
 'htmlhelp': HTMLHelpBuilder,
Modified: doctools/trunk/sphinx/config.py
==============================================================================
--- doctools/trunk/sphinx/config.py	(original)
+++ doctools/trunk/sphinx/config.py	Sun Jan 27 21:23:25 2008
@@ -10,29 +10,10 @@
 """
 
 import os
-import sys
 import types
 from os import path
 
 
-class ConfigError(Exception):
- """Raised if something's wrong with the configuration."""
-
- def __init__(self, message, orig_exc=None):
- self.message = message
- self.orig_exc = orig_exc
-
- def __repr__(self):
- if self.orig_exc:
- return 'ConfigError(%r, %r)' % (self.message, self.orig_exc)
- return 'ConfigError(%r)' % self.message
-
- def __str__(self):
- if self.orig_exc:
- return '%s (exception: %s)' % (self.message, self.orig_exc)
- return self.message
-
-
 class Config(object):
 """Configuration file abstraction."""
 
@@ -53,7 +34,6 @@
 
 # general reading options
 unused_files = ([], True),
- refcount_file = ('', True),
 add_function_parentheses = (True, True),
 add_module_names = (True, True),
 
@@ -77,6 +57,7 @@
 )
 
 def __init__(self, dirname, filename):
+ self.values = self.config_values.copy()
 config = {}
 olddir = os.getcwd()
 try:
@@ -90,14 +71,10 @@
 del config[key]
 self.__dict__.update(config)
 
- def __getattr__(self, name):
- if name in self.config_values:
- defval = self.config_values[name][0]
- setattr(self, name, defval)
- return defval
- if name[0:1] == '_':
- return object.__getattr__(self, name)
- raise AttributeError('no configuration value named %r' % name)
+ def init_defaults(self):
+ for val in self.values:
+ if val not in self.__dict__:
+ self.__dict__[val] = self.values[val][0]
 
 def __getitem__(self, name):
 return getattr(self, name)
Modified: doctools/trunk/sphinx/directives.py
==============================================================================
--- doctools/trunk/sphinx/directives.py	(original)
+++ doctools/trunk/sphinx/directives.py	Sun Jan 27 21:23:25 2008
@@ -15,7 +15,7 @@
 from os import path
 
 from docutils import nodes
-from docutils.parsers.rst import directives, roles
+from docutils.parsers.rst import directives
 from docutils.parsers.rst.directives import admonitions
 
 from sphinx import addnodes
@@ -273,21 +273,6 @@
 return optname
 
 
-def add_refcount_annotation(env, node, name):
- """Add a reference count annotation. Return None."""
- entry = env.refcounts.get(name)
- if not entry:
- return
- elif entry.result_type not in ("PyObject*", "PyVarObject*"):
- return
- rc = 'Return value: '
- if entry.result_refs is None:
- rc += "Always NULL."
- else:
- rc += (entry.result_refs and "New" or "Borrowed") + " reference."
- node += addnodes.refcount(rc, rc)
-
-
 def desc_directive(desctype, arguments, options, content, lineno,
 content_offset, block_text, state, state_machine):
 env = state.document.settings.env
@@ -361,8 +346,6 @@
 fullname, fullname)
 
 subnode = addnodes.desc_content()
- if desctype == 'cfunction':
- add_refcount_annotation(env, subnode, name)
 # needed for automatic qualification of members
 clsname_set = False
 if desctype == 'class' and names:
@@ -407,8 +390,8 @@
 'describe',
 ]
 
-for name in desctypes:
- directives.register_directive(name, desc_directive)
+for _name in desctypes:
+ directives.register_directive(_name, desc_directive)
 
 
 # ------ versionadded/versionchanged -----------------------------------------------
Modified: doctools/trunk/sphinx/environment.py
==============================================================================
--- doctools/trunk/sphinx/environment.py	(original)
+++ doctools/trunk/sphinx/environment.py	Sun Jan 27 21:23:25 2008
@@ -44,7 +44,6 @@
 
 from sphinx import addnodes
 from sphinx.util import get_matching_files, os_path, SEP
-from sphinx.refcounting import Refcounts
 
 default_settings = {
 'embed_stylesheet': False,
@@ -128,7 +127,7 @@
 
 class MyStandaloneReader(standalone.Reader):
 """
- Add our own Substitutions transform.
+ Add our own transforms.
 """
 def get_transforms(self):
 tf = standalone.Reader.get_transforms(self)
@@ -189,9 +188,6 @@
 self.srcdir = srcdir
 self.config = None
 
- # refcount data if present
- self.refcounts = {}
-
 # the docutils settings for building
 self.settings = default_settings.copy()
 self.settings['env'] = self
@@ -314,7 +310,7 @@
 
 return added, changed, removed
 
- def update(self, config, hook=None):
+ def update(self, config, app=None):
 """(Re-)read all files new or changed since last update. Yields a summary
 and then filenames as it processes them. Store all environment filenames
 in the canonical format (ie using SEP as a separator in place of
@@ -341,11 +337,6 @@
 
 self.config = config
 
- # read the refcounts file
- if self.config.refcount_file:
- self.refcounts = Refcounts.fromfile(
- path.join(self.srcdir, self.config.refcount_file))
-
 # clear all files no longer present
 for filename in removed:
 self.clear_file(filename)
@@ -353,14 +344,14 @@
 # read all new and changed files
 for filename in added + changed:
 yield filename
- self.read_file(filename)
+ self.read_file(filename, app=app)
 
 if 'contents.rst' not in self.all_files:
 self._warnfunc('no master file contents.rst found')
 
 # --------- SINGLE FILE BUILDING -------------------------------------------
 
- def read_file(self, filename, src_path=None, save_parsed=True, hook=None):
+ def read_file(self, filename, src_path=None, save_parsed=True, app=None):
 """Parse a file and add/update inventory entries for the doctree.
 If srcpath is given, read from a different source file."""
 # remove all inventory entries for that file
@@ -386,9 +377,8 @@
 f.close()
 self.all_files[filename] = (path.getmtime(src_path), md5sum)
 
- # run post-read hook
- if hook:
- hook(doctree)
+ if app:
+ app.emit('doctree-read', doctree)
 
 # make it picklable
 doctree.reporter = None
@@ -590,7 +580,7 @@
 for includefile in includefiles:
 try:
 toc = self.tocs[includefile].deepcopy()
- except KeyError, err:
+ except KeyError:
 # this is raised if the included file does not exist
 self._warnfunc('%s: toctree contains ref to nonexisting '
 'file %r' % (filename, includefile))
@@ -622,6 +612,9 @@
 return doctree
 
 
+ descroles = frozenset(('data', 'exc', 'func', 'class', 'const', 'attr',
+ 'meth', 'cfunc', 'cdata', 'ctype', 'cmacro'))
+
 def resolve_references(self, doctree, docfilename, builder):
 for node in doctree.traverse(addnodes.pending_xref):
 contnode = node[0].deepcopy()
@@ -702,7 +695,7 @@
 (platform and '(%s) ' % platform),
 synopsis, (deprecated and ' (deprecated)' or ''))
 newnode.append(contnode)
- else:
+ elif typ in self.descroles:
 # "descrefs"
 modname = node['modname']
 clsname = node['classname']
@@ -720,11 +713,16 @@
 builder.get_relative_uri(docfilename, desc[0])
 + '#' + name)
 newnode.append(contnode)
+ else:
+ raise RuntimeError('unknown xfileref node encountered: %s' % node)
 except NoUri:
 newnode = contnode
 if newnode:
 node.replace_self(newnode)
 
+ # allow custom references to be resolved
+ builder.app.emit('doctree-resolved', doctree, docfilename)
+
 def create_index(self, builder, _fixre=re.compile(r'(.*) ([(][^()]*[)])')):
 """Create the real index from the collected index entries."""
 new = {}
Deleted: /doctools/trunk/sphinx/extension.py
==============================================================================
--- /doctools/trunk/sphinx/extension.py	Sun Jan 27 21:23:25 2008
+++ (empty file)
@@ -1,83 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.extension
- ~~~~~~~~~~~~~~~~
-
- Gracefully adapted from the TextPress event system by Armin.
-
- :copyright: 2008 by Georg Brandl, Armin Ronacher.
- :license: BSD.
-"""
-
-from sphinx.config import ConfigError
-
-
-def import_object(objname, source=None):
- """Import an object from a 'module.name' string."""
- try:
- module, name = objname.rsplit('.', 1)
- except ValueError, err:
- raise ConfigError('Invalid full object name %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
- try:
- return getattr(__import__(module, None, None, [name]), name)
- except ImportError, err:
- raise ConfigError('Could not import %s' % module +
- (source and ' (needed for %s)' % source or ''), err)
- except AttributeError, err:
- raise ConfigError('Could not find %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
-
-
-# List of all known events. Maps name to arguments description.
-events = {
- 'builder-created' : 'builder instance',
- 'doctree-read' : 'the doctree before being pickled',
-}
-
-class EventManager(object):
- """
- Helper class that handles event listeners and events.
-
- This is *not* a public interface. Always use the emit_event()
- functions to access it or the connect_event() / disconnect_event()
- functions on the application.
- """
-
- def __init__(self):
- self.next_listener_id = 0
- self._listeners = {}
-
- def _validate(self, event):
- event = intern(event)
- if event not in events:
- raise RuntimeError('unknown event name: %s' % event)
-
- def connect(self, event, callback):
- self._validate(event)
- listener_id = self.next_listener_id
- if event not in self._listeners:
- self._listeners[event] = {listener_id: callback}
- else:
- self._listeners[event][listener_id] = callback
- self.next_listener_id += 1
- return listener_id
-
- def remove(self, listener_id):
- for event in self._listeners:
- event.pop(listener_id, None)
-
- def emit(self, event, *args):
- self._validate(event)
- if event in self._listeners:
- for listener_id, callback in self._listeners[event].iteritems():
- yield listener_id, callback(*args)
-
-
-class DummyEventManager(EventManager):
- def connect(self, event, callback):
- self._validate(event)
- def remove(self, listener_id):
- pass
- def emit(self, event, *args):
- self._validate(event)
Modified: doctools/trunk/sphinx/latexwriter.py
==============================================================================
--- doctools/trunk/sphinx/latexwriter.py	(original)
+++ doctools/trunk/sphinx/latexwriter.py	Sun Jan 27 21:23:25 2008
@@ -14,9 +14,8 @@
 
 import re
 import time
-import string
 
-from docutils import frontend, nodes, languages, writers, utils
+from docutils import nodes, writers
 
 from sphinx import addnodes
 from sphinx import highlighting
Deleted: /doctools/trunk/sphinx/refcounting.py
==============================================================================
--- /doctools/trunk/sphinx/refcounting.py	Sun Jan 27 21:23:25 2008
+++ (empty file)
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.refcounting
- ~~~~~~~~~~~~~~~~~~
-
- Handle reference counting annotations, based on refcount.py
- and anno-api.py.
-
- :copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
-"""
-
-class RCEntry:
- def __init__(self, name):
- self.name = name
- self.args = []
- self.result_type = ''
- self.result_refs = None
-
-
-class Refcounts(dict):
- @classmethod
- def fromfile(cls, filename):
- d = cls()
- fp = open(filename, 'r')
- try:
- for line in fp:
- line = line.strip()
- if line[:1] in ("", "#"):
- # blank lines and comments
- continue
- parts = line.split(":", 4)
- if len(parts) != 5:
- raise ValueError("Wrong field count in %r" % line)
- function, type, arg, refcount, comment = parts
- # Get the entry, creating it if needed:
- try:
- entry = d[function]
- except KeyError:
- entry = d[function] = RCEntry(function)
- if not refcount or refcount == "null":
- refcount = None
- else:
- refcount = int(refcount)
- # Update the entry with the new parameter or the result information.
- if arg:
- entry.args.append((arg, type, refcount))
- else:
- entry.result_type = type
- entry.result_refs = refcount
- finally:
- fp.close()
- return d
Modified: doctools/trunk/sphinx/util/__init__.py
==============================================================================
--- doctools/trunk/sphinx/util/__init__.py	(original)
+++ doctools/trunk/sphinx/util/__init__.py	Sun Jan 27 21:23:25 2008
@@ -51,15 +51,6 @@
 raise
 
 
-def status_iterator(iterable, colorfunc=lambda x: x, stream=sys.stdout):
- """Print out each item before yielding it."""
- for item in iterable:
- print >>stream, colorfunc(item),
- stream.flush()
- yield item
- print >>stream
-
-
 def get_matching_files(dirname, pattern, exclude=()):
 """Get all files matching a pattern in a directory, recursively."""
 # dirname is a normalized absolute path.
Modified: doctools/trunk/sphinx/util/console.py
==============================================================================
--- doctools/trunk/sphinx/util/console.py	(original)
+++ doctools/trunk/sphinx/util/console.py	Sun Jan 27 21:23:25 2008
@@ -11,6 +11,30 @@
 
 codes = {}
 
+def get_terminal_width():
+ """Borrowed from the py lib."""
+ try:
+ import os, termios, fcntl, struct
+ call = fcntl.ioctl(0, termios.TIOCGWINSZ, "000円"*8)
+ height, width = struct.unpack("hhhh", call)[:2]
+ terminal_width = width
+ except (SystemExit, KeyboardInterrupt):
+ raise
+ except:
+ # FALLBACK
+ terminal_width = int(os.environ.get('COLUMNS', 80))-1
+ return terminal_width
+
+_tw = get_terminal_width()
+
+def print_and_backspace(text, func):
+ if not codes:
+ # if no coloring, don't output fancy backspaces
+ func(text)
+ else:
+ func(text.ljust(_tw) + _tw * "\b")
+
+
 def nocolor():
 codes.clear()
 
@@ -31,8 +55,8 @@
 'blink': '05m',
 }
 
-for name, value in _attrs.items():
- codes[name] = '\x1b[' + value
+for _name, _value in _attrs.items():
+ codes[_name] = '\x1b[' + _value
 
 _colors = [
 ('black', 'darkgray'),
@@ -49,5 +73,5 @@
 codes[dark] = '\x1b[%im' % (i+30)
 codes[light] = '\x1b[%i;01m' % (i+30)
 
-for name in codes:
- create_color_func(name)
+for _name in codes:
+ create_color_func(_name)
Modified: doctools/trunk/sphinx/util/smartypants.py
==============================================================================
--- doctools/trunk/sphinx/util/smartypants.py	(original)
+++ doctools/trunk/sphinx/util/smartypants.py	Sun Jan 27 21:23:25 2008
@@ -151,7 +151,7 @@
 (\s | s\b)
 """ % (close_class,), re.VERBOSE)
 
-def educateQuotes(str):
+def educateQuotes(s):
 """
 Parameter: String.
 
@@ -163,35 +163,33 @@
 
 # Special case if the very first character is a quote
 # followed by punctuation at a non-word-break. Close the quotes by brute force:
- str = single_quote_start_re.sub("&#8217;", str)
- str = double_quote_start_re.sub("&#8221;", str)
+ s = single_quote_start_re.sub("&#8217;", s)
+ s = double_quote_start_re.sub("&#8221;", s)
 
 # Special case for double sets of quotes, e.g.:
 # <p>He said, "'Quoted' words in a larger quote."</p>
- str = double_quote_sets_re.sub("&#8220;&#8216;", str)
- str = single_quote_sets_re.sub("&#8216;&#8220;", str)
+ s = double_quote_sets_re.sub("&#8220;&#8216;", s)
+ s = single_quote_sets_re.sub("&#8216;&#8220;", s)
 
 # Special case for decade abbreviations (the '80s):
- str = decade_abbr_re.sub("&#8217;", str)
+ s = decade_abbr_re.sub("&#8217;", s)
 
- str = opening_single_quotes_regex.sub(r"1円&#8216;", str)
- str = closing_single_quotes_regex.sub(r"1円&#8217;", str)
- str = closing_single_quotes_regex_2.sub(r"1円&#8217;2円", str)
+ s = opening_single_quotes_regex.sub(r"1円&#8216;", s)
+ s = closing_single_quotes_regex.sub(r"1円&#8217;", s)
+ s = closing_single_quotes_regex_2.sub(r"1円&#8217;2円", s)
 
 # Any remaining single quotes should be opening ones:
- str = str.replace("'", "&#8216;")
+ s = s.replace("'", "&#8216;")
 
- str = opening_double_quotes_regex.sub(r"1円&#8220;", str)
- str = closing_double_quotes_regex.sub(r"&#8221;", str)
- str = closing_double_quotes_regex_2.sub(r"1円&#8221;", str)
+ s = opening_double_quotes_regex.sub(r"1円&#8220;", s)
+ s = closing_double_quotes_regex.sub(r"&#8221;", s)
+ s = closing_double_quotes_regex_2.sub(r"1円&#8221;", s)
 
 # Any remaining quotes should be opening ones.
- str = str.replace('"', "&#8220;")
+ return s.replace('"', "&#8220;")
 
- return str
 
-
-def educateBackticks(str):
+def educateBackticks(s):
 """
 Parameter: String.
 Returns: The string, with ``backticks'' -style double quotes
@@ -199,10 +197,10 @@
 Example input: ``Isn't this fun?''
 Example output: &#8220;Isn't this fun?&#8221;
 """
- return str.replace("``", "&#8220;").replace("''", "&#8221;")
+ return s.replace("``", "&#8220;").replace("''", "&#8221;")
 
 
-def educateSingleBackticks(str):
+def educateSingleBackticks(s):
 """
 Parameter: String.
 Returns: The string, with `backticks' -style single quotes
@@ -211,10 +209,10 @@
 Example input: `Isn't this fun?'
 Example output: &#8216;Isn&#8217;t this fun?&#8217;
 """
- return str.replace('`', "&#8216;").replace("'", "&#8217;")
+ return s.replace('`', "&#8216;").replace("'", "&#8217;")
 
 
-def educateDashesOldSchool(str):
+def educateDashesOldSchool(s):
 """
 Parameter: String.
 
@@ -222,10 +220,10 @@
 an en-dash HTML entity, and each "---" translated to
 an em-dash HTML entity.
 """
- return str.replace('---', "&#8212;").replace('--', "&#8211;")
+ return s.replace('---', "&#8212;").replace('--', "&#8211;")
 
 
-def educateDashesOldSchoolInverted(str):
+def educateDashesOldSchoolInverted(s):
 """
 Parameter: String.
 
@@ -240,11 +238,11 @@
 the shortcut should be shorter to type. (Thanks to Aaron
 Swartz for the idea.)
 """
- return str.replace('---', "&#8211;").replace('--', "&#8212;")
+ return s.replace('---', "&#8211;").replace('--', "&#8212;")
 
 
 
-def educateEllipses(str):
+def educateEllipses(s):
 """
 Parameter: String.
 Returns: The string, with each instance of "..." translated to
@@ -253,7 +251,7 @@
 Example input: Huh...?
 Example output: Huh&#8230;?
 """
- return str.replace('...', "&#8230;").replace('. . .', "&#8230;")
+ return s.replace('...', "&#8230;").replace('. . .', "&#8230;")
 
 
 __author__ = "Chad Miller <smartypantspy at chad.org>"


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /