Code Monkey home page Code Monkey logo

markdown-include's Introduction

Markdown-Include

This is an extension to Python-Markdown which provides an "include" function, similar to that found in LaTeX (and also the C pre-processor and Fortran). I originally wrote it for my FORD Fortran auto-documentation generator.

Installation

This module can now be installed using pip.

pip install markdown-include

Tests

Use the unittest module

python -m unittest discover unittests/

Usage

This module can be used in a program in the following way:

import markdown
html = markdown.markdown(source, extensions=['markdown_include.include'])

Markdown-Include can also be included in MkDocs projects like below:

markdown_extensions:
    - markdown_include.include:
        base_path: docs

The syntax for use within your Markdown files is {!filename!}. This statement will be replaced by the contents of filename. Markdown-Include will work recursively, so any included files within filename will also be included. This replacement is done prior to any other Markdown processing, so any Markdown syntax that you want can be used within your included files. Note that this is a change from the previous version. It was felt that this syntax was less likely to conflict with any code fragments present in the Markdown.

By default, all file-names are evaluated relative to the location from which Markdown is being called. If you would like to change the directory relative to which paths are evaluated, then this can be done by specifying the extension setting base_path.

Line Ranges

You can also define specific lines or line ranges to include by specifying lines:

{!filename!lines=1  3 8-10  2}

lines takes a sequence of integers separated by spaces (one or more), or it can also take line ranges specified with a start line and an end line separated by a dash (-).

In the example above, it would read the file called filename and include the lines 1, 3, 8, 9, 10, 2.

Notice that line 9 was not explicitly set. But it was still included as part of the range 8-10.

Also, notice that line 2 is set after the range 8-10. This means that the line 2 in filename will be included after (below) the range 8-10.

You can use this to include lines in a different order than the original file. But it also means that if you want to preserve the original order, you have to pay attention to the order in which you specify the lines.

Configuration

The following settings can be specified when initialising the plugin.

  • base_path: Default location from which to evaluate relative paths for the include statement. (Default: the run-directory.)
  • encoding: Encoding of the files used by the include statement. (Default: utf-8.)
  • inheritHeadingDepth : If true, increases headings on include file by amount of previous heading. Combiens with headingOffset option, below. (Default: False.)
  • headingOffset: Increases heading depth by a specific ammount, in addition to the inheritHeadingDepth Option. (Default: 0)
  • throwException: When true, if the extension is unable to find an included file it will throw an exception which the user can catch. If false (default), a warning will be printed and Markdown will continue parsing the file.

Examples

An example of setting the base path and file encoding is given below:

import markdown
from markdown_include.include import MarkdownInclude

# Markdown Extensions
markdown_include = MarkdownInclude(
    configs={'base_path':'/srv/content/', 'encoding': 'iso-8859-1'}
)
html = markdown.markdown(source, extensions=[markdown_include])

Included files can inherit the heading depth of the location inheritHeadingDepth, as well as receive a specific offset, headingOffset For example, consider the files

Source file
# Heading Level 1 of main file

{!included_file.md!}

## Heading Level 2 of main file

{!included_file.md!}

and included_file.md

# This heading will be one level deeper from the previous heading
More included file content.
End of included content.

Then running the script

import markdown
from markdown_include.include import MarkdownInclude

# Markdown Extensions
markdown_include = MarkdownInclude(
    configs={'inheritHeadingDepth':True}
)
html = markdown.markdown(source, extensions=[markdown_include])

produces

<p>Source file</p>
<h1>Heading Level 1 of main file</h1>
<h2>This heading will be one level deeper from the previous heading</h2>
<p>More included file content.</p>
<p>End of included content.</p>
<h2>Heading Level 2 of main file</h2>
<h3>This heading will be one level deeper from the previous heading</h3>
<p>More included file content.</p>
<p>End of included content.</p>

ChangeLog

Version 0.7.0

Modified to work with Python-Markdown 3.4. This makes the plugin incompatible with versions < 3.0.

Version 0.6.0

  • Added ability ot offset headers in the included file so they fall under the header level in which the include occurs
  • Add option to throw exception when can't find an include file (instead of printing a warning)
  • Fixed stripping of last character in file, so only occurs if it is a new-line
  • Some behind-the-scenes improvement to code and documentation

Version 0.5.1

Bugfix for a syntax error.

Version 0.5

Corrected some errors in documentation and merged in commits of diegobz to add support for encoding and tidy up the source code.

Version 0.4

Fixed problem related to passing configurations to the extension.

Version 0.3

Added support for Python 3.

Version 0.2

Changed the API to be less likely to conflict with other syntax.

Version 0.1

Initial release.

markdown-include's People

Contributors

cmacmackin avatar diegobz avatar karlssonjohan avatar larivact avatar patcorwin avatar sharat87 avatar sieboldianus avatar tiangolo avatar umaaz avatar vsimko avatar zedthree avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

markdown-include's Issues

Allow caller to handle exceptions

Thanks for the nice library! We've been using it in Zulip (http://github.com/zulip/zulip/) in hundreds of plcaes, but one significant problem is that this library doesn't throw a proper exception in the event that the file to be included isn't present. This makes it hard to check for typoes when using this library. See e.g.
zulip/zulip#10947 for details.

Would you be willing to consider a change (at least under an option) to make this library not catch-and-print the exception for an invalid file?

Support multiple includes on one line

I've got a potential use-case that populates a requirements table using includes, like this:

Product Operating System Cloud Environments Datacenter Environments
Product X {! ./prod-x-os.md !} {! ./prod-x-cloud.md !} {! ./prod-x-dc.md !}
Product Y {! ./prod-y-os.md !} {! ./prod-y-cloud.md !} {! ./prod-y-dc.md !}

markdown-include only matches first include on each line, so the above scenario doesn't work. In fact, in this example scenario, everything from the second include onward is actually lost.

How do you feel about supporting multiple matches per line?

Include file reference does not work with relative path

So I just tried deploying some docs using includes via Jenkins and for whatever reason it won't find the files. I'm using the extension with Mkdocs and it gives me the following type of messages:

Warning: could not find file changelog/5.3.1-hotfix2.md. Ignoring include statement. Error: [Errno 2] No such file or directory: u'changelog/5.3.1-hotfix2.md'
Warning: could not find file changelog/5.3.1-hotfix1.md. Ignoring include statement. Error: [Errno 2] No such file or directory: u'changelog/5.3.1-hotfix1.md'

The full filename is /data/workspace///docs/changelog/5.3.1-hotfix2.md

I have set "base_path" to "/docs/" and it works locally on Mac OS X 10.11.4 and on my CentOS testing machine. But on the jenkins machine running CentOS 7 as well it can't find the files.

When I change the filename references to absolute paths to the files it works but this is not desirable for my implementation. Any ideas? Markdown and the extension should be called by mkdocs just like it does locally.

Using 'inheritHeadingDepth: True' in MkDocs config

I have the markdown-include extension working with MkDocs, but when I try to add the inheritHeadingDepth: True config value, I get an error. I'm guessing I just don't have the config syntax right. Can anyone advise? Thanks :)

From my mkdocs.yml

markdown_extensions:
    - markdown_include.include:
        base_path: docs
        inheritHeadingDepth: True

Config instructions: https://github.com/cmacmackin/markdown-include#configuration
I have tried formatting the YML several different ways with no luck.

Debug says:

DEBUG   -  Config value: 'markdown_extensions' = [{}, u'extra', u'admonition', {}, u'sane_lists']

And the error:

ERROR   -  Config value: 'markdown_extensions'. Error: inheritHeadingDepth
[E 190516 17:55:31 ioloop:801] Exception in callback <bound method type.poll_tasks of <class 'livereload.handlers.LiveReloadHandler'>>
    Traceback (most recent call last):
      File "c:\python27\lib\site-packages\tornado\ioloop.py", line 1229, in _run
        return self.callback()
      File "c:\python27\lib\site-packages\livereload\handlers.py", line 67, in poll_tasks
        filepath, delay = cls.watcher.examine()
      File "c:\python27\lib\site-packages\livereload\watcher.py", line 73, in examine
        func and func()
      File "c:\python27\lib\site-packages\mkdocs\commands\serve.py", line 107, in builder
        site_dir=site_dir
      File "c:\python27\lib\site-packages\mkdocs\config\base.py", line 210, in load_config
        "Aborted with {0} Configuration Errors!".format(len(errors))
    ConfigurationError: Aborted with 1 Configuration Errors!

0.7.0 release doesn't have markdown version update?

The release notes for 0.7.0 say it's been modified to work with 3.4 of markdown, making it incompatible with anything less than 3.0, but when I look in setup.cfg with the release tag of 0.7.0, nothing regarding dependencies changed? Was this a mistake or intentional?

regex automatically escapes underscore at the beginning of the filename

When I try to include a file with a name starting with an underscore, eg.

{!tutorial/_setup.md!}

I get the following error message:

Warning: could not find file docs/tutorial/\_setup.md. Ignoring include statement. Error: [Errno 2] No such file or directory: 'docs/tutorial/\\_setup.md'

sdist is missing on PyPI for 0.7.0

Currently there is only a wheel on PyPI for version 0.7.0, but no sdist. For packaging this project on conda-forge it would be nice to have an sdist as well

cc @hadim

OrderedDict Error in v0.6.0

The recent release has broken my documentation build

Here is the relevant error message.

/home/travis/build/D3DEnergetic/FIDASIM/docs/fidasim.md
Traceback (most recent call last):
  File "/home/travis/virtualenv/python2.7.14/bin/ford", line 11, in <module>
    sys.exit(run())
  File "/home/travis/virtualenv/python2.7.14/lib/python2.7/site-packages/ford/__init__.py", line 385, in run
    proj_data, proj_docs, md = initialize()
  File "/home/travis/virtualenv/python2.7.14/lib/python2.7/site-packages/ford/__init__.py", line 141, in initialize
    extension_configs={'markdown_include.include': {'base_path': md_base}})
  File "/home/travis/virtualenv/python2.7.14/lib/python2.7/site-packages/markdown/__init__.py", line 159, in __init__
    configs=kwargs.get('extension_configs', {}))
  File "/home/travis/virtualenv/python2.7.14/lib/python2.7/site-packages/markdown/__init__.py", line 187, in registerExtensions
    ext.extendMarkdown(self, globals())
  File "/home/travis/virtualenv/python2.7.14/lib/python2.7/site-packages/markdown_include/include.py", line 58, in extendMarkdown
    md.preprocessors.register(IncludePreprocessor(md,self.getConfigs()), 'include', 101)
AttributeError: 'OrderedDict' object has no attribute 'register'
make: *** [docs] Error 1

Extension can have infinite circular reference loop

I accidentally set up a circular reference in my page structure that brought my machine to its knees. If you include a page in itself it will endlessly try to do so (as designed).

Now, if you have multiple users like I do that use includes to build documents how can I prevent them from creating circular references?

They might (and very likely will) include a page that unbeknownst to them includes the same page a few levels of include iterations down?

Can the extension check itself or over the "include tree" that it's parsing? If not can there be a debug message or something that tells me if this is happening?

The last character is always stripped from imported files

markdown-include==0.5.1 seems to always strip the last character from files it includes. That's great when the last is a new line character \n, but less great when it's something else like the end of an html tag.

See this example. Note how the trailing > has been stripped off the included file and the markdown is therefore messed up.

import markdown
from markdown_include.include import MarkdownInclude
from devtools import debug

included_file_content = """
<b>this is a test</b>"""

with open('docs/examples/error.html', 'w') as f:
    f.write(included_file_content)

# Markdown Extensions
markdown_include = MarkdownInclude(
    configs={'base_path': 'docs/examples/'}
)

src = """
This is a test

{!error.html!}

and more afterwards.
"""
html = markdown.markdown(src, extensions=[markdown_include])
debug(html)
"""
test.py:24 <module>
    html: (
        '<p>This is a test</p>\n'
        '<p><b>this is a test&lt;/b</p>\n'
        '<p>and more afterwards.</p>'
    ) (str) len=80
"""

Include file relative to processed markdown file

It would be great if there was an option to include a file relative to the markdown file being processed.

For example if we have the following directory structure.

.
├── a.md
└── sub
    ├── b.md
    └── c.md

To include c.md from b.md someting simlar to {!./c.md!} could be used. To include a.md from b.md something similar to {!../a.md!} could be used.

Remove frontmatter from 'included' markdown files

When my markdown files contain YAML or Multi-markdown style metadata, I've found that markdown-include doesn't process it out (prehaps because of the way the replacement is handled?). Is there a way I can exclude the frontmatter from the file being imported so that only the content will replace the {!tag!}?

Ability to include portions of the file

I'm writing a user guide and it would be nice to include section of the external file. Either by specifying line numbers or named section. I previously used asciidoc include feature and found this capability extremely useful.

Referring to actual tested code makes sure that your examples in docs are not out of date, but you also don't want to include noise like imports, etc... And often just a small part of a class demonstrate a particular aspect you're describing.

http://asciidoctor.org/docs/user-manual/#include-basics
E.g.

  • include::filename.txt[lines="1..10,15..20"]
  • include::core.rb[tags=parse]

I'm not sure how you'd specify this exactly but perhaps some variation of :

{! include filename[lines=1..20] !}
{! include filename[tags=snippet1] !}

Incompatible with

It seems that all includes used in child projects fail to render when using mkdocs-multirepo-plugin which is by far the most popular solution for combining multiple mkdocs sites into a single one.

What I observed is that the include renders as empty in this case, probably because it fails to find the file to include.

Feature Request: Remote files

I have recently come across the use case where I need to include a markdown file that is in another git repo. What is your opinion on making it possible to do this.

As an example if could be:

{! https://raw.githubusercontent.com/<user>/<repo>/<branch>/docs/some_file.md !}

It could detect the urls easily enough download to a temp file and then include as normal.

Links in included files have to be written like if they were in the parent including file

Hi,

I'm having an issue with my includes because included files contains links.

For instance I have the following structure and parameter base_path: docs:

.
├── docs
│   ├── Tutorials
│   │   └── Examples
│   │   │   ├── Example1.md
│   │   │   ├── Example2.md
│   │   │   └── Example3.md
│   ├── global
│   │   └── Postman.md
│   ├── postman
│   │   └── collection1.json
└── mkdocs.yml

NOTE: this is a simplified directory structure for the ticket, the real one is visible at https://github.com/hyperledger/besu-docs/

Examples files all include the Postman.md file with the include syntax {!global/Postman.md!} relative to the docs base_path directory.

The Postman.md includes some explanation and a link to the JSON file in the form [download the collection](/postman/collection1.json) for it to be valid when the HTML is compiles with MkDocs and put on the website. I'm forced to use / as I can't know where the include is going to be made and so I can't have a relative path like postman/collection1.json otherwise it will end up in having the file relative for example to Tutorials/Examples/Example1 and will point to Tutorials/Examples/Example1/postman/collection1.json which is not right.

But the side effects of that are:

  1. I have to make sure the /postman/collection1.json is a valid link because for instance with ReadTheDocs, path includes /lang/version/ so I have to add a redirect from /postman/collection1.json to /lang/version/postman/collection1.json which is not convenient
  2. Link checker (is use the Node markdown-link-check but I guess all checkers will have the same issue) is failing on the link in the Postman.md file as it's valid only when included and compiles with MkDocs.
  3. I'm unable to navigate to the link in my markdown source using my IDE as the link is invalid before being compiled in HTML.
  4. Google indexed the Github repos and when people look at the Github rendered Postman.md file, the link is invalid as it tries to point to the root of my repos like https://github.com/me/myproject/blob/master/postman/collection1.json instead of https://github.com/me/myproject/blob/master/docs/postman/collection1.json

My wish:

Having the links in included files able to be written relatively to the included file then translated to the correct path once included would fix all this.

For instance, when I include the link in my docs/global/Postman.md I would simply write [download the collection](../postman/collection1.json), which would be valid when not included and once included in docs/Tutorials/Examples/Example1.md it would be translated to [download the collection](../../postman/collection1.json) which would correctly go up by two levels as expected.

The rendered HTML will have a valid relative path and the source markdown would be valid too whatever the context.

I think it requires the rewriting of URL just after including and before rendering the HTML or whatever, but I don't see any drawbacks for the moment. Please tell me if you do.

Thanks.

How can I install this along with MkDocs?

Hello! I'm confused with the instructions in the README, I'm able to include this extension in MkDocs:
markdown_extensions: [markdown_include.include]
Now, I don't have an idea how to use this extension, Here what I want to achieve is to create a file name file.md with a content of:

[URL1]: mydocs/file1.md
[URL2]: mydocs/file2.md
[URL3]: mydocs/file3.md

then calling these relative paths from other markdown files like this:

Here is the [link][URL1] 
Here is the [link][URL2] 
Here is the [link][URL3] 

Is it possible? thanks in advance!

Extension is not context aware

Not sure this is actually a bug, I can see how this is "works as designed" but might be problematic.

The intended use of the include string is "place the string somewhere and there it will include the referenced file". The problem is that if you place the string inside a code block (inline, fenced doesn't matter) to demonstrate how it is used it will process the string regardless. For an existing file you will simply get the file included at the location of the string, for a non-existing file there will be a warning.

So it's impossible to use the include string inside a code block. You can encode the braces with HTML characters outside of a code block to make an example but everything else won't work. I'm trying to teach my users how to use the extension and it breaks consistency with the rest of my examples.

More dynamic line ranges

Current line ranges can only support fixed line numbers, which means you cannot:

  1. get "all lines after line 5". Currently, you could use lines=5-99999 but this is not robust and raises a warning.
  2. Include the "last 10 lines" since you do not know these line numbers

Potential fixes could use syntax like:

  1. lines=5-END or lines=5-
  2. lines=-10-END following the Python slicing syntax where negative indices count from the end

Breaking my site with latest updates

I use this plugin on my site after upgrading

- markdown_include.include:
      base_path: .

The error I get is:

mkdocs serve
INFO     -  Building documentation...
ERROR    -  Config value: 'markdown_extensions'. Error: MarkdownInclude.extendMarkdown() missing 1 required positional argument: 'md_globals'
Aborted with 1 Configuration Errors!

Output from requirements.txt file:

click==8.1.3
fontawesome-markdown @ https://github.com/bmcorser/fontawesome-markdown/archive/master.zip
ghp-import==2.1.0
gitdb==4.0.9
GitPython==3.1.27
importlib-metadata==4.12.0
Jinja2==3.1.2
livereload==2.6.3
lunr==0.6.2
Markdown==3.4.1
markdown-include==0.6.0
MarkupSafe==2.1.1
mercurial==6.1.4
mergedeep==1.3.4
mkdocs==1.3.0
mkdocs-autolinks-plugin==0.6.0
mkdocs-git-revision-date-localized-plugin==1.1.0
mkdocs-material==8.3.9
mkdocs-material-extensions==1.0.3
packaging==21.3
pycurl==7.45.1
Pygments==2.12.0
pymdown-extensions==9.5
pyparsing==3.0.9
python-dateutil==2.8.2
pytz==2022.1
PyYAML==6.0
pyyaml_env_tag==0.1
six==1.16.0
smmap==5.0.0
tabulate==0.8.10
tornado==6.2
watchdog==2.1.9
zipp==3.8.1```

Include by header

Instead of including content by line number, it would be nice to include it by section heading. This should include all content up to the next heading of equal or greater importance.

For example, given the following document called readme.md

Lorem Ipsum

Usage

Installation

Install with pip install lorem-ipsum

Importing

import lorem_ipsum

Issues

Contact me at [email protected]

Then you could include the "Usage" section (up to and excluding ## Issues) with

{!readme.md!section=usage}

Rather than needing to know the exact line numbers. This syntax plays nicely with the existing Markdown syntax where you can link to this section heading with #usage

Including CommonMark-tables with inheritHeadingDepth renders the table as text

In my mkdocs project, I am using markdown-include and mkdocs-material.

Let a top-level file be:

# Some heading

{!tables/Some_Table.md!}

And let table/Some_Table.md be:

The following table shows some data about whatever.

| Some first column | Some second column | 
| ----------------- | ------------------ |
| Some text.        | Some more text.    |
  • With inheritHeadingDepth being false, the generated output will be some nice looking HTML-table.
  • With inheritHeadingDepth being true, the generated output will be the actual text, wrapped in <p> elements.

Used versions:

  • mkdocs: 1.1.1
  • mkdocs-material: 6.2.8
  • markdown-include: 0.6.0
  • python: 3.8

inheritHeadingDepth should refer to the original including file's depth

With inheritHeadingDepth being true.

Let a top-level file be:

# Some first level heading

{!sub/Subsection_1.md!}

{!task/Subsection_2.md!}

{!task/Subsection_3.md!}

And let each task/Subection_X.md be:

# Some heading <X>

Some text.

Then, I would expect the result to be:

# Some first level heading

## Some heading 1

Some text.

## Some heading 2

Some text.

## Some heading 3

Some text.

But, the actual result is:

# Some first level heading

## Some heading 1

Some text.

### Some heading 2

Some text.

#### Some heading 3

Some text.

With the current implementation of inheritHeadingDepth, it is impossible to include several sections on the same level.

Current workaround probably is to move the heading from the sections into the parent file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.