Add new env method to get the real path to a file reference, and use it.

This commit is contained in:
Georg Brandl
2010-08-23 13:07:19 +00:00
parent 62fc1ab87d
commit 398fb3f00c
3 changed files with 40 additions and 45 deletions

View File

@@ -7,10 +7,8 @@
:license: BSD, see LICENSE for details.
"""
import os
import sys
import codecs
from os import path
from docutils import nodes
from docutils.parsers.rst import Directive, directives
@@ -93,23 +91,11 @@ class LiteralInclude(Directive):
def run(self):
document = self.state.document
filename = self.arguments[0]
if not document.settings.file_insertion_enabled:
return [document.reporter.warning('File insertion disabled',
line=self.lineno)]
env = document.settings.env
if filename.startswith('/') or filename.startswith(os.sep):
rel_fn = filename[1:]
else:
docdir = path.dirname(env.doc2path(env.docname, base=None))
rel_fn = path.join(docdir, filename)
try:
fn = path.join(env.srcdir, rel_fn)
except UnicodeDecodeError:
# the source directory is a bytestring with non-ASCII characters;
# let's try to encode the rel_fn in the file system encoding
rel_fn = rel_fn.encode(sys.getfilesystemencoding())
fn = path.join(env.srcdir, rel_fn)
rel_filename, filename = env.relfn2path(self.arguments[0])
if 'pyobject' in self.options and 'lines' in self.options:
return [document.reporter.warning(
@@ -119,7 +105,7 @@ class LiteralInclude(Directive):
encoding = self.options.get('encoding', env.config.source_encoding)
codec_info = codecs.lookup(encoding)
try:
f = codecs.StreamReaderWriter(open(fn, 'rb'),
f = codecs.StreamReaderWriter(open(filename, 'rb'),
codec_info[2], codec_info[3], 'strict')
lines = f.readlines()
f.close()
@@ -136,7 +122,7 @@ class LiteralInclude(Directive):
objectname = self.options.get('pyobject')
if objectname is not None:
from sphinx.pycode import ModuleAnalyzer
analyzer = ModuleAnalyzer.for_file(fn, '')
analyzer = ModuleAnalyzer.for_file(filename, '')
tags = analyzer.find_tags()
if objectname not in tags:
return [document.reporter.warning(
@@ -178,13 +164,13 @@ class LiteralInclude(Directive):
text = ''.join(lines)
if self.options.get('tab-width'):
text = text.expandtabs(self.options['tab-width'])
retnode = nodes.literal_block(text, text, source=fn)
retnode = nodes.literal_block(text, text, source=filename)
retnode.line = 1
if self.options.get('language', ''):
retnode['language'] = self.options['language']
if 'linenos' in self.options:
retnode['linenos'] = True
document.settings.env.note_dependency(rel_fn)
env.note_dependency(rel_filename)
return [retnode]

View File

@@ -369,14 +369,13 @@ from docutils.parsers.rst.directives.misc import Include as BaseInclude
class Include(BaseInclude):
"""
Like the standard "Include" directive, but interprets absolute paths
correctly.
"correctly", i.e. relative to source directory.
"""
def run(self):
if self.arguments[0].startswith('/') or \
self.arguments[0].startswith(os.sep):
env = self.state.document.settings.env
self.arguments[0] = os.path.join(env.srcdir, self.arguments[0][1:])
env = self.state.document.settings.env
rel_filename, filename = env.relfn2path(self.arguments[0])
self.arguments[0] = filename
return BaseInclude.run(self)

View File

@@ -433,6 +433,27 @@ class BuildEnvironment:
else:
return path.join(base, docname) + suffix
def relfn2path(self, filename, docname=None):
"""Return paths to a file referenced from a document, relative to
documentation root and absolute.
Absolute filenames are relative to the source dir, while relative
filenames are relative to the dir of the containing document.
"""
if filename.startswith('/') or filename.startswith(os.sep):
rel_fn = filename[1:]
else:
docdir = path.dirname(self.doc2path(docname or self.docname,
base=None))
rel_fn = path.join(docdir, filename)
try:
return rel_fn, path.join(self.srcdir, rel_fn)
except UnicodeDecodeError:
# the source directory is a bytestring with non-ASCII characters;
# let's try to encode the rel_fn in the file system encoding
enc_rel_fn = rel_fn.encode(sys.getfilesystemencoding())
return rel_fn, path.join(self.srcdir, enc_rel_fn)
def find_files(self, config):
"""Find all source files in the source dir and put them in
self.found_docs.
@@ -806,25 +827,19 @@ class BuildEnvironment:
def process_downloads(self, docname, doctree):
"""Process downloadable file paths. """
docdir = path.dirname(self.doc2path(docname, base=None))
for node in doctree.traverse(addnodes.download_reference):
targetname = node['reftarget']
if targetname.startswith('/') or targetname.startswith(os.sep):
# absolute
filepath = targetname[1:]
else:
filepath = path.normpath(path.join(docdir, node['reftarget']))
self.dependencies.setdefault(docname, set()).add(filepath)
if not os.access(path.join(self.srcdir, filepath), os.R_OK):
self.warn(docname, 'download file not readable: %s' % filepath,
rel_filename, filename = self.relfn2path(targetname, docname)
self.dependencies.setdefault(docname, set()).add(rel_filename)
if not os.access(filename, os.R_OK):
self.warn(docname, 'download file not readable: %s' % filename,
getattr(node, 'line', None))
continue
uniquename = self.dlfiles.add_file(docname, filepath)
uniquename = self.dlfiles.add_file(docname, filename)
node['filename'] = uniquename
def process_images(self, docname, doctree):
"""Process and rewrite image URIs."""
docdir = path.dirname(self.doc2path(docname, base=None))
for node in doctree.traverse(nodes.image):
# Map the mimetype to the corresponding image. The writer may
# choose the best image from these candidates. The special key * is
@@ -837,16 +852,11 @@ class BuildEnvironment:
node.line)
candidates['?'] = imguri
continue
# imgpath is the image path *from srcdir*
if imguri.startswith('/') or imguri.startswith(os.sep):
# absolute path (= relative to srcdir)
imgpath = path.normpath(imguri[1:])
else:
imgpath = path.normpath(path.join(docdir, imguri))
rel_imgpath, full_imgpath = self.relfn2path(imguri, docname)
# set imgpath as default URI
node['uri'] = imgpath
if imgpath.endswith(os.extsep + '*'):
for filename in glob(path.join(self.srcdir, imgpath)):
node['uri'] = rel_imgpath
if rel_imgpath.endswith(os.extsep + '*'):
for filename in glob(full_imgpath):
new_imgpath = relative_path(self.srcdir, filename)
if filename.lower().endswith('.pdf'):
candidates['application/pdf'] = new_imgpath
@@ -866,7 +876,7 @@ class BuildEnvironment:
if imgtype:
candidates['image/' + imgtype] = new_imgpath
else:
candidates['*'] = imgpath
candidates['*'] = rel_imgpath
# map image paths to unique image names (so that they can be put
# into a single directory)
for imgpath in candidates.itervalues():