Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pathconverter: Add file:// prefix to paths converted to absolute #1620

Merged
merged 9 commits into from
Mar 3, 2022
1 change: 1 addition & 0 deletions docs/src/markdown/extensions/pathconverter.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ Option | Type | Default | Description
`relative_path` | string | `#!py3 ''` | A string indicating an absolute path that the references are to be relative to (not used when `absolute` is set `True`).
`absolute` | bool | `#!py3 False` | Determines whether paths are converted to absolute or relative.
`tags` | string | `#!py3 'a script img link'` | Tags (separated by spaces) that are searched to find `href` and `src` attributes.
`file_scheme` | bool | `#!py3 False` | Produce file:// URLs instead of raw paths when converting paths to absolute.
32 changes: 20 additions & 12 deletions pymdownx/pathconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def repl_relative(m, base_path, relative_path):
return link


def repl_absolute(m, base_path):
def repl_absolute(m, base_path, file_scheme):
"""Replace path with absolute path."""

link = m.group(0)
Expand All @@ -105,28 +105,34 @@ def repl_absolute(m, base_path):
path = util.url2path(path)
path = os.path.normpath(os.path.join(base_path, path))
path = util.path2url(path)
start = '/' if not path.startswith('/') else ''
link = '%s"%s%s"' % (
m.group('name'),
start,
urlunparse((scheme, netloc, path, params, query, fragment))
)
if file_scheme:
link = '%s"%s"' % (
m.group('name'),
urlunparse(("file", netloc, path, params, query, fragment))
)
else:
start = '/' if not path.startswith('/') else ''
link = '%s"%s%s"' % (
m.group('name'),
start,
urlunparse((scheme, netloc, path, params, query, fragment))
)
except Exception: # pragma: no cover
# Parsing crashed and burned; no need to continue.
pass

return link


def repl(m, base_path, rel_path=None):
def repl(m, base_path, rel_path=None, file_scheme=None):
"""Replace."""

if m.group('avoid'):
tag = m.group('avoid')
else:
tag = m.group('open')
if rel_path is None:
tag += RE_TAG_LINK_ATTR.sub(lambda m2: repl_absolute(m2, base_path), m.group('attr'))
tag += RE_TAG_LINK_ATTR.sub(lambda m2: repl_absolute(m2, base_path, file_scheme), m.group('attr'))
else:
tag += RE_TAG_LINK_ATTR.sub(lambda m2: repl_relative(m2, base_path, rel_path), m.group('attr'))
tag += m.group('close')
Expand All @@ -142,11 +148,12 @@ def run(self, text):
basepath = self.config['base_path']
relativepath = self.config['relative_path']
absolute = bool(self.config['absolute'])
filescheme = bool(self.config['file_scheme'])
tags = re.compile(RE_TAG_HTML % '|'.join(self.config['tags'].split()))
if not absolute and basepath and relativepath:
text = tags.sub(lambda m: repl(m, basepath, relativepath), text)
text = tags.sub(lambda m: repl(m, basepath, rel_path=relativepath), text)
elif absolute and basepath:
text = tags.sub(lambda m: repl(m, basepath), text)
text = tags.sub(lambda m: repl(m, basepath, file_scheme=filescheme), text)
return text


Expand All @@ -160,7 +167,8 @@ def __init__(self, *args, **kwargs):
'base_path': ["", "Base path used to find files - Default: \"\""],
'relative_path': ["", "Path that files will be relative to (not needed if using absolute) - Default: \"\""],
'absolute': [False, "Paths are absolute by default; disable for relative - Default: False"],
'tags': ["img script a link", "tags to convert src and/or href in - Default: 'img scripts a link'"]
'tags': ["img script a link", "tags to convert src and/or href in - Default: 'img scripts a link'"],
'file_scheme': [False, "Use file:// scheme for absolute paths - Default: False"],
}

super(PathConverterExtension, self).__init__(*args, **kwargs)
Expand Down
47 changes: 47 additions & 0 deletions tests/test_extensions/test_pathconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,27 @@ def test_mail(self):
)


class TestAbsoluteFileScheme(TestAbsolute):
"""Test absolute paths with file:// scheme."""

extension = ["pymdownx.pathconverter"]
extension_configs = {
"pymdownx.pathconverter": {
"base_path": "/Some/fake/path",
"absolute": True,
"file_scheme": True,
}
}

def test_relative_path(self):
"""Test relative path."""

self.check_markdown(
r'![picture](./test_extensions/_assets/bg.png)',
r'<p><img alt="picture" src="file:///Some/fake/path/test_extensions/_assets/bg.png" /></p>'
)


class TestWindowsAbs(util.MdCase):
"""Test windows specific cases for absolute."""

Expand All @@ -300,6 +321,32 @@ def test_windows_root_conversion(self):
)


class TestWindowsAbsFileScheme(util.MdCase):
"""Test windows specific cases for absolute with file:// scheme."""

extension = ["pymdownx.pathconverter"]
extension_configs = {
"pymdownx.pathconverter": {
"base_path": "C:/Some/fake/path",
"absolute": True,
"file_scheme": True,
}
}

def test_windows_root_conversion(self):
"""Test Windows c:/ Conversion."""
if util.is_win():
self.check_markdown(
r'![picture](./extensions/_assets/bg.png)',
r'<p><img alt="picture" src="file:///C:/Some/fake/path/extensions/_assets/bg.png" /></p>'
)
else:
self.check_markdown(
r'![picture](./extensions/_assets/bg.png)',
r'<p><img alt="picture" src="file:///C%3A/Some/fake/path/extensions/_assets/bg.png" /></p>'
)


class TestWindowsRel(util.MdCase):
"""Test windows specific cases for relative."""

Expand Down