Comment 10 for bug 914221

Revision history for this message
Edward K. Ream (edreamleo) wrote : Re: [Bug 914221] Re: bound method ViewRenderedController.update

On Sat, Jan 14, 2012 at 8:35 AM, Edward K. Ream <email address hidden> wrote:

> So the question is, how does docutils create the (weird) relative path::
>
>    '../../../../../lib/site-packages/docutils/writers/html4css1/html4css1.css'
>
> One would think that this path would yield the desired .css file only
> if it was invoked five (!) levels below Python's lib directory.  In
> other words, where do all the "../" come from?

I still don't know the answer to the question, but I'm pretty sure I
know where the path is computed and where the error message comes
from.

The message comes from the ctor of the FileInput class in the file
site-customize/docutils/io.py

The actual path,
'../../../../../lib/site-packages/docutils/writers/html4css1/html4css1.css',
most likely comes from the following very strange code at the start of
the Writer class in the file site-customize/docutils/__init__.py:

class Writer(writers.Writer):

    supported = ('html', 'html4css1', 'xhtml')
    """Formats this writer supports."""

    default_stylesheet = 'html4css1.css'

    ### g.pdb('writer/init.py') ### EKR.

    default_stylesheet_path = utils.relative_path(
        os.path.join(os.getcwd(), 'dummy'),
        os.path.join(os.path.dirname(__file__), default_stylesheet))

Note that this code is *not* in the ctor: the default_stylesheet_path
is a class var.

For me, this results in a full, absolute path name. I just now as I
write this see the call to utils.relative_path. Presumably this
creates all the '../' components of the path. Let's see...

def relative_path(source, target):
    """
    Build and return a path to `target`, relative to `source` (both files).

    If there is no common prefix, return the absolute path to `target`.
    """
    source_parts = os.path.abspath(source or 'dummy_file').split(os.sep)
    target_parts = os.path.abspath(target).split(os.sep)
    # Check first 2 parts because '/dir'.split('/') == ['', 'dir']:
    if source_parts[:2] != target_parts[:2]:
        # Nothing in common between paths.
        # Return absolute path, using '/' for URLs:
        return '/'.join(target_parts)
    source_parts.reverse()
    target_parts.reverse()
    while (source_parts and target_parts
           and source_parts[-1] == target_parts[-1]):
        # Remove path components in common:
        source_parts.pop()
        target_parts.pop()
    target_parts.reverse()
    parts = ['..'] * (len(source_parts) - 1) + target_parts
    return '/'.join(parts)

Without understanding the code in detail, the last two lines do indeed
look like the source of the '../' components of the path.

In short, it looks like os.getcwd() is returning a path that results
in utils.relative_path returning
'../../../../../lib/site-packages/docutils/writers/html4css1/html4css1.css'

This is quite bizarre, imo. I wouldn't be surprised if this is a bug
in docutils. All this code seems too clever by half.

The question is, what to do about it.

Edward