| import logging |
| import os |
| |
| from recommonmark import transform |
| """ |
| Allow linking of Markdown documentation from the source code tree into the Sphinx |
| documentation tree. |
| |
| The Markdown documents will have links relative to the source code root, rather |
| than the place they are now linked too - this code fixes these paths up. |
| |
| We also want links from two Markdown documents found in the Sphinx docs to |
| work, so that is also fixed up. |
| """ |
| |
| |
| def path_contains(parent_path, child_path): |
| """Check a path contains another path. |
| |
| >>> path_contains("a/b", "a/b") |
| True |
| >>> path_contains("a/b", "a/b/") |
| True |
| >>> path_contains("a/b", "a/b/c") |
| True |
| >>> path_contains("a/b", "c") |
| False |
| >>> path_contains("a/b", "c/b") |
| False |
| >>> path_contains("a/b", "c/../a/b/d") |
| True |
| >>> path_contains("../a/b", "../a/b/d") |
| True |
| >>> path_contains("../a/b", "../a/c") |
| False |
| >>> path_contains("a", "abc") |
| False |
| >>> path_contains("aa", "abc") |
| False |
| """ |
| # Append a separator to the end of both paths to work around the fact that |
| # os.path.commonprefix does character by character comparisons rather than |
| # path segment by path segment. |
| parent_path = os.path.join(os.path.normpath(parent_path), '') |
| child_path = os.path.join(os.path.normpath(child_path), '') |
| common_path = os.path.commonprefix((parent_path, child_path)) |
| return common_path == parent_path |
| |
| |
| def relative(parent_dir, child_path): |
| """Get the relative between a path that contains another path.""" |
| child_dir = os.path.dirname(child_path) |
| assert path_contains(parent_dir, child_dir), "{} not inside {}".format( |
| child_path, parent_dir) |
| return os.path.relpath(child_path, start=parent_dir) |
| |
| |
| class MarkdownCodeSymlinks(transform.AutoStructify, object): |
| docs_root_dir = os.path.realpath(os.path.dirname(__file__)) |
| code_root_dir = os.path.realpath(os.path.join(docs_root_dir, "..", "..")) |
| |
| mapping = { |
| 'docs2code': {}, |
| 'code2docs': {}, |
| } |
| |
| @classmethod |
| def relative_code(cls, url): |
| """Get a value relative to the code directory.""" |
| return relative(cls.code_root_dir, url) |
| |
| @classmethod |
| def relative_docs(cls, url): |
| """Get a value relative to the docs directory.""" |
| return relative(cls.docs_root_dir, url) |
| |
| @classmethod |
| def add_mapping(cls, docs_rel, code_rel): |
| assert docs_rel not in cls.mapping['docs2code'], """\ |
| Assertion error! Document already in mapping! |
| New Value: {} |
| Current Value: {} |
| """.format(docs_rel, cls.mapping['docs2code'][docs_rel]) |
| assert code_rel not in cls.mapping['code2docs'], """\ |
| Assertion error! Document already in mapping! |
| New Value: {} |
| Current Value: {} |
| """.format(docs_rel, cls.mapping['code2docs'][code_rel]) |
| |
| cls.mapping['docs2code'][docs_rel] = code_rel |
| cls.mapping['code2docs'][code_rel] = docs_rel |
| |
| @classmethod |
| def find_links(cls): |
| """Walk the docs dir and find links to docs in the code dir.""" |
| for root, dirs, files in os.walk(cls.docs_root_dir): |
| for fname in files: |
| fpath = os.path.abspath(os.path.join(root, fname)) |
| |
| if not os.path.islink(fpath): |
| continue |
| |
| link_path = os.path.join(root, os.readlink(fpath)) |
| # Is link outside the code directory? |
| if not path_contains(cls.code_root_dir, link_path): |
| continue |
| |
| # Is link internal to the docs directory? |
| if path_contains(cls.docs_root_dir, link_path): |
| continue |
| |
| docs_rel = cls.relative_docs(fpath) |
| code_rel = cls.relative_code(link_path) |
| |
| cls.add_mapping(docs_rel, code_rel) |
| import pprint |
| pprint.pprint(cls.mapping) |
| |
| @property |
| def url_resolver(self): |
| return self._url_resolver |
| |
| @url_resolver.setter |
| def url_resolver(self, value): |
| print(self, value) |
| |
| # Resolve a link from one markdown to another document. |
| def _url_resolver(self, ourl): |
| """Resolve a URL found in a markdown file.""" |
| assert self.docs_root_dir == os.path.realpath(self.root_dir), """\ |
| Configuration error! Document Root != Current Root |
| Document Root: {} |
| Current Root: {} |
| """.format(self.docs_root_dir, self.root_dir) |
| |
| src_path = os.path.abspath(self.document['source']) |
| src_dir = os.path.dirname(src_path) |
| dst_path = os.path.abspath(os.path.join(self.docs_root_dir, ourl)) |
| dst_rsrc = os.path.relpath(dst_path, start=src_dir) |
| |
| src_rdoc = self.relative_docs(src_path) |
| |
| print |
| print("url_resolver") |
| print(src_path) |
| print(dst_path) |
| print(dst_rsrc) |
| print(src_rdoc) |
| |
| # Is the source document a linked one? |
| if src_rdoc not in self.mapping['docs2code']: |
| # Don't do any rewriting on non-linked markdown. |
| url = ourl |
| |
| # Is the destination also inside docs? |
| elif dst_rsrc not in self.mapping['code2docs']: |
| # Return a path to the GitHub repo. |
| url = "{}/blob/master/{}".format( |
| self.config['github_code_repo'], dst_rsrc) |
| else: |
| url = os.path.relpath( |
| os.path.join( |
| self.docs_root_dir, self.mapping['code2docs'][dst_rsrc]), |
| start=src_dir) |
| base_url, ext = os.path.splitext(url) |
| assert ext in (".md", |
| ".markdown"), ("Unknown extension {}".format(ext)) |
| url = "{}.html".format(base_url) |
| |
| print("---") |
| print(ourl) |
| print(url) |
| print |
| return url |
| |
| |
| if __name__ == "__main__": |
| import doctest |
| doctest.testmod() |