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

Resolve relative links in long_description #163

Open
protolambda opened this issue Mar 27, 2020 · 8 comments
Open

Resolve relative links in long_description #163

protolambda opened this issue Mar 27, 2020 · 8 comments

Comments

@protolambda
Copy link

What's the problem this feature will solve?

Broken relative links in many pypi hosted project descriptions.

Describe the solution you'd like

Add a project maintainer option that sets what relative links should be resolved to.

A similar feature is available in html itself: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

Even better would be if we can use some properties of the package such as version to describe the path to resolve relative links with.

E.g. github.com/example/foobar/tree/v$VERSION configured as base path.

Additional context

A common use-case is to take your project readme, and load it into the package long-description.
Often this is done with:

with open("README.md", "rt", encoding="utf8") as f:
    readme = f.read()
...
    long_description=readme,
    long_description_content_type="text/markdown",

This works, but relative links break. And hardcoding is a poor practice too, as e.g. GitHub-hosted Readme links become stuck to a certain version, instead of referencing the resource on the same version the viewer is on.
Also, sometimes a resource moves, and to keep older documentation working some ability to point to a different absolute path would be great.

PyPi as a viewer of the description can resolve these relative links.
And maybe even default to the home page (which lots of projects set to their github project link), to un-break thousands of broken relative description links currently in PyPi.

@di di transferred this issue from pypi/warehouse Mar 28, 2020
@di di added the enhancement label Mar 28, 2020
@Mattwmaster58
Copy link

Here's an example of a project with this issue: https://pypi.org/project/black/

@protolambda
Copy link
Author

If it helps, one of the bigger projects I work on has a specs-repository that enables all code snippets to be used as a package. Because the project is so documentation heavy, the local links are all relative, and the readme all broken. https://pypi.org/project/eth2spec/

Another alternative to implement a solution, without the need for a pypi project setting of some kind, is to read the setting from the readme itself. If pypi can read some markdown comment like <-- pypi: relative_base=https://github.com/foobar/example/ --> then links can be made to work.

But better would be to fix all those existing broken links and support it with a pypi project setting.

@di
Copy link
Member

di commented Apr 7, 2020

I think we could probably do this by supporting some custom front-matter for Markdown descriptions.

We'd want to be able to support the same functionality for rST-formatted descriptions as well though, and I'm not familiar enough with rST to say how to do that.

@jamadden
Copy link
Member

jamadden commented Apr 7, 2020

We'd want to be able to support the same functionality for rST-formatted descriptions as well though, and I'm not familiar enough with rST to say how to do that.

The Nikola static blog engine supports extensive metadata in both markdown and reST. While it can just use specially formatted comments, it can also use the reST standard "docinfo" nodes, which is what I personally use:

How to make money
=================

:slug: how-to-make-money
:date: 2012-09-15 19:52:05 UTC

These have the advantage of being parsed by docutils itself; there are a set of standard tags, and arbitrary custom tags can be used as well. Getting the data is easy:

        meta = {}
        if 'title' in document:
            meta['title'] = document['title']
        for docinfo in document.traverse(docutils.nodes.docinfo):
            for element in docinfo.children:
                if element.tagname == 'field':  # custom fields (e.g. summary)
                    name_elem, body_elem = element.children
                    name = name_elem.astext()
                    value = body_elem.astext()
                elif element.tagname == 'authors':  # author list
                    name = element.tagname
                    value = [element.astext() for element in element.children]
                else:  # standard fields (e.g. address)
                    name = element.tagname
                    value = element.astext()
                name = name.lower()

                meta[name] = value

If tools are unaware of custom tags, they format in HTML as a field list (basically a table). (This is opposed to a custom directive, which can do anything from produce an error in the rendered document to nothing at all, depending on settings.) Nikola strips the docinfo nodes so they don't show up in rendering, which is pretty easy:

        for node in self.document.traverse(docutils.nodes.docinfo):
            node.parent.remove(node)

@miketheman
Copy link
Member

Related to #71

@chamini2
Copy link

Tried to use the HTML base approach and the tag is instead printed in the readme. (https://pypi.org/project/fal/0.8.1/)
image

Can we enable base HTML tag for this approach to work manually at least?

@solaluset
Copy link

As a workaround, it's possible to modify links dynamically with regex:

GITHUB_URL = "https://github.com/your/repo"
long_description = open("README.md").read()
# links on PyPI should have absolute URLs
long_description = re.sub(
    r"(\[[^\]]+\]\()((?!https?:)[^\)]+)(\))",
    lambda m: m.group(1) + GITHUB_URL + "/blob/master/" + m.group(2) + m.group(3),
    long_description,
)

@zcutlip
Copy link

zcutlip commented Nov 3, 2023

As a workaround, it's possible to modify links dynamically with regex:

Interestingly this regex also work for links to directories. In this case GitHub sees you're asking for a directory in the tree and helpfully redirects you from blob/main/directory-name/ to tree/main/directory-name/

Just thought I'd share in case someone stumbles upon this solution with a similar need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants