SF.net SVN: docutils:[9995] trunk/docutils
milde--- via Docutils-checkins <[email protected]>
| Newsgroups | gmane.text.docutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
Revision: 9995
http://sourceforge.net/p/docutils/code/9995
Author: milde
Date: 2024-12-04 13:32:53 +0000 (Wed, 04 Dec 2024)
Log Message:
-----------
Update/fix URI reference to filesystem path conversion.
Use "pathlib" instead of "os.path".
Return a `pathlib.Path` instance instead of a `str`.
Use Path.from_uri() for "file" URIs:
* Provide a backport for Python < 3.13.
* Don't apply "root_prefix" to "file:" URIs.
* "file:" URIs with relative path are invalid and now raise an Error.
Based on patch https://github.com/AA-Turner/docutils/pull/18
by Adam Turner.
Modified Paths:
--------------
trunk/docutils/docs/ref/doctree.rst
trunk/docutils/docs/user/config.rst
trunk/docutils/docutils/writers/_html_base.py
trunk/docutils/docutils/writers/html4css1/__init__.py
trunk/docutils/test/functional/input/data/embed_images.rst
trunk/docutils/test/test_writers/test_html5_polyglot_parts.py
Modified: trunk/docutils/docs/ref/doctree.rst
===================================================================
--- trunk/docutils/docs/ref/doctree.rst 2024-12-04 13:32:39 UTC (rev 9994)
+++ trunk/docutils/docs/ref/doctree.rst 2024-12-04 13:32:53 UTC (rev 9995)
@@ -4490,6 +4490,9 @@
The ``uri`` attribute is used in the `\<image>`_ and `\<figure>`_
elements to refer to the image via a `URI Reference`_ [#]_. [rfc3986]_
+The `root_prefix`_ configuration setting is applied when a URI Reference
+starting with "/" is converted to a local filesystem path.
+
.. [#] Examples are a full URI, an *absolute-path reference* (begins with
a single slash character) or a *relative-path reference* (does not
begin with a slash character).
@@ -5198,6 +5201,7 @@
.. _id_prefix: ../user/config.html#id-prefix
.. _image_loading: ../user/config.html#image-loading
.. _report_level: ../user/config.html#report-level
+.. _root_prefix: ../user/config.html#root-prefix
.. _stylesheet: ../user/config.html#stylesheet
.. _syntax_highlight: ../user/config.html#syntax-highlight
Modified: trunk/docutils/docs/user/config.rst
===================================================================
--- trunk/docutils/docs/user/config.rst 2024-12-04 13:32:39 UTC (rev 9994)
+++ trunk/docutils/docs/user/config.rst 2024-12-04 13:32:53 UTC (rev 9995)
@@ -588,17 +588,28 @@
Base directory, prepended to a filesystem path__ starting with "/" when
including files with the `"include"`_, `"raw"`_, or `"csv-table"`_
directives.
+Also applied to the `"uri" attribute`_ of an <image> or <figure> starting
+with "/" when it is converted to a local filesystem path.
+Not applied to absolute Windows paths and ``file:`` URIs.
-Also applied when a writer converts an image URI__ to a local filesystem
-path in order to determine the image size or embed the image in the output.
+Example:
+ The HTML server for a documentation project serves files from the
+ "DocumentRoot" ``/var/www/html/``.
+ Image files are stored in a dedicated directory ``/var/www/html/pictures/``.
-:Default: "".
+ With ``root-prefix=/var/www/html``, the rST "image" directive ::
+
+ .. image:: /pictures/mylogo.png
+
+ works for LaTeX output and HTML output with embedded images as well as
+ for HTML output with images included via URI reference.
+
+:Default: "" (empty string).
:Option: ``--root-prefix``.
New in Docutils 0.21.
__ ../ref/rst/directives.html#path
-__ ../ref/rst/directives.html#uri
sectnum_xform
@@ -609,7 +620,7 @@
with the `"sectnum" directive`_.
If disabled, section numbers might be added to the output by the
-renderer (e.g. by LaTeX or via a CSS style definition).
+renderer (e.g. by CSS style rules or by LaTeX).
:Default: True.
:Options: ``--section-numbering``, ``--no-section-numbering``.
Modified: trunk/docutils/docutils/writers/_html_base.py
===================================================================
--- trunk/docutils/docutils/writers/_html_base.py 2024-12-04 13:32:39 UTC (rev 9994)
+++ trunk/docutils/docutils/writers/_html_base.py 2024-12-04 13:32:53 UTC (rev 9995)
@@ -25,8 +25,8 @@
import os
import os.path
import re
+import sys
import urllib.parse
-import urllib.request
import warnings
import xml.etree.ElementTree as ET
from pathlib import Path
@@ -460,7 +460,7 @@
reading_problems.append('Reading external files disabled.')
if not reading_problems:
try:
- imagepath = self.uri2imagepath(uri)
+ imagepath = self.uri2path(uri)
with PIL.Image.open(imagepath) as img:
imgsize = img.size
except (ValueError, OSError, UnicodeEncodeError) as err:
@@ -626,38 +626,47 @@
return
child['classes'].append(class_)
- def uri2imagepath(self, uri):
- """Get POSIX filesystem path corresponding to an URI.
+ def uri2path(self, uri: str) -> Path:
+ """Return filesystem path corresponding to a `URI reference`__.
- The image directive expects an image URI__. Some writers require the
- corresponding image path to read the image size from the file or to
- embed the image in the output.
+ The `root_prefix`__ setting` is applied to URI references starting
+ with "/" (but not to absolute Windows paths or "file" URIs).
- URIs with absolute "path" part consider the ``root_prefix`` setting.
+ If the `output_path`__ setting is not empty, relative paths are
+ adjusted. (To work in the output document, URI references with
+ relative path relate to the output directory. For access by the
+ writer, paths must be relative to the working directory.)
- In order to work in the output document, URI references with relative
- path relate to the output directory. For access by the writer, the
- corresponding image path must be relative to the current working
- directory.
+ Use case:
+ The <image> element refers to the image via a "URI reference".
+ The corresponding filesystem path is required to read the
+ image size from the file or to embed the image in the output.
+ A filesystem path is also expected by the "LaTeX" output format
+ (with relative paths unchanged, relating to the output directory).
+
Provisional: the function's location, interface and behaviour
may change without advance warning.
- __ https://www.rfc-editor.org/rfc/rfc3986.html
+ __ https://www.rfc-editor.org/rfc/rfc3986.html#section-4.1
+ __ https://docutils.sourceforge.io/docs/user/config.html#root-prefix
+ __ https://docutils.sourceforge.io/docs/user/config.html#output-path
"""
- destination = self.settings.output_path or ''
- uri_parts = urllib.parse.urlparse(uri)
- if uri_parts.scheme not in ('', 'file'):
- raise ValueError('Can only read local images.')
- imagepath = urllib.parse.unquote(uri_parts.path)
- if self.settings.root_prefix and imagepath.startswith('/'):
- root_prefix = Path(self.settings.root_prefix)
- imagepath = (root_prefix/imagepath.removeprefix('/')).as_posix()
- elif not os.path.isabs(imagepath): # path may be absolute Windows path
- destdir = os.path.abspath(os.path.dirname(destination))
- imagepath = utils.relative_path(None,
- os.path.join(destdir, imagepath))
- return imagepath
+ if uri.startswith('file:'):
+ return Path.from_uri(uri)
+ uri_parts = urllib.parse.urlsplit(uri)
+ if uri_parts.scheme != '':
+ raise ValueError(f'Cannot get file path corresponding to {uri}.')
+ # extract and adjust path from "relative URI reference"
+ path = urllib.parse.unquote(uri_parts.path)
+ if self.settings.root_prefix and path.startswith('/'):
+ return Path(self.settings.root_prefix) / path.removeprefix('/')
+ path = Path(path)
+ if self.settings.output_path and not path.is_absolute():
+ # rewrite relative paths, but not "d:/foo" or similar
+ dest_dir = Path(self.settings.output_path).parent.resolve()
+ path = Path(utils.relative_path(None, dest_dir/path))
+ return path
def visit_Text(self, node) -> None:
text = node.astext()
@@ -1187,7 +1196,7 @@
atts['loading'] = 'lazy'
elif loading == 'embed':
try:
- imagepath = self.uri2imagepath(uri)
+ imagepath = self.uri2path(uri)
if mimetype == 'image/svg+xml':
imagedata = Path(imagepath).read_text(encoding='utf-8')
else:
@@ -1911,3 +1920,40 @@
visit_substitution_definition = ignore_node
visit_target = ignore_node
visit_pending = ignore_node
+
+
+# Backport `pathlib.Path.from_uri()` class method:
+
+if sys.version_info[:2] < (3, 13):
+ import pathlib
+
+ # subclassing from Path must consider the OS flavour
+ # https://stackoverflow.com/questions/29850801/subclass-pathlib-path-fails
+ class Path(type(pathlib.Path())): # noqa F811 (redefinition of 'Path')
+ """`pathlib.Path` with `from_uri()` classmethod backported from 3.13.
+ """
+ # copied from
+ # https://github.com/python/cpython/blob/3.13/Lib/pathlib/_local.py
+ # with minor adaptions
+ @classmethod
+ def from_uri(cls, uri):
+ """Return a new path from the given 'file' URI."""
+ if not uri.startswith('file:'):
+ raise ValueError(f"URI does not start with 'file:': {uri!r}")
+ path = uri[5:]
+ if path[:3] == '///':
+ # Remove empty authority
+ path = path[2:]
+ elif path[:12] == '//localhost/':
+ # Remove 'localhost' authority
+ path = path[11:]
+ if path[:3] == '///' or (path[:1] == '/' and path[2:3] in ':|'):
+ # Remove slash before DOS device/UNC path
+ path = path[1:]
+ if path[1:2] == '|':
+ # Replace bar with colon in DOS drive
+ path = path[:1] + ':' + path[2:]
+ path = cls(urllib.parse.unquote(path))
+ if not path.is_absolute():
+ raise ValueError(f"URI is not absolute: {uri!r}")
+ return path
Modified: trunk/docutils/docutils/writers/html4css1/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/html4css1/__init__.py 2024-12-04 13:32:39 UTC (rev 9994)
+++ trunk/docutils/docutils/writers/html4css1/__init__.py 2024-12-04 13:32:53 UTC (rev 9995)
@@ -572,7 +572,7 @@
if (PIL and ('width' not in node or 'height' not in node)
and self.settings.file_insertion_enabled):
try:
- imagepath = self.uri2imagepath(uri)
+ imagepath = self.uri2path(uri)
with PIL.Image.open(imagepath) as img:
img_size = img.size
except (ValueError, OSError, UnicodeEncodeError) as e:
@@ -579,8 +579,7 @@
self.document.reporter.warning(
f'Problem reading image file: {e}')
else:
- self.settings.record_dependencies.add(
- imagepath.replace('\\', '/'))
+ self.settings.record_dependencies.add(imagepath.as_posix())
if 'width' not in atts:
atts['width'] = '%dpx' % img_size[0]
if 'height' not in atts:
Modified: trunk/docutils/test/functional/input/data/embed_images.rst
===================================================================
--- trunk/docutils/test/functional/input/data/embed_images.rst 2024-12-04 13:32:39 UTC (rev 9994)
+++ trunk/docutils/test/functional/input/data/embed_images.rst 2024-12-04 13:32:53 UTC (rev 9995)
@@ -6,7 +6,7 @@
directly included, other images are base64_ encoded and included
as a `data URI`_.
-.. figure:: file:../../../docs/user/rst/images/biohazard.png
+.. figure:: ../../../docs/user/rst/images/biohazard.png
:alt: biohazard
:align: left
:width: 2em
@@ -15,7 +15,7 @@
Embedded PNG image in a figure.
-.. figure:: file:../../../docs/user/rst/images/biohazard-scaling.svg
+.. figure:: ../../../docs/user/rst/images/biohazard-scaling.svg
:alt: biohazard
:align: right
:width: 2em
Modified: trunk/docutils/test/test_writers/test_html5_polyglot_parts.py
===================================================================
--- trunk/docutils/test/test_writers/test_html5_polyglot_parts.py 2024-12-04 13:32:39 UTC (rev 9994)
+++ trunk/docutils/test/test_writers/test_html5_polyglot_parts.py 2024-12-04 13:32:53 UTC (rev 9995)
@@ -23,12 +23,6 @@
import docutils.core
from docutils.writers import html5_polyglot
-# TEST_ROOT is ./test/ from the docutils root
-TEST_ROOT = Path(__file__).parents[1]
-DATA_ROOT = TEST_ROOT / 'data'
-ROOT_PREFIX = (TEST_ROOT / 'functional/input').as_posix()
-
-
# Parts returned by `publish_parts()` for the HTML5 writer by default:
# * empty input string
# * default configuration settings.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.