Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ classifiers=[
dynamic = ["version"]
dependencies = [
"mkdocs>=1.0",
"pandas>=1.3", # pd.read_xml() was added in pandas 1.3
"pandas>=2.1", # pd.DataFrame.map() was added in pandas 2.1
"pyyaml>=5.4.1",
"tabulate>=0.8.7",
]
Expand Down Expand Up @@ -125,8 +125,12 @@ dev-dependencies = [
"mkdocs-material>=9.6.9",
"openpyxl>=3.1.5",
"pyarrow>=17.0.0",
# used by pd.read_spss()
"pyreadstat>=1.2.8",
"pytest>=8.3.5",
"pytest-cov>=5.0.0",
"ruff",
# used by pd.read_hdf()
"tables>=3.9.2",
"xlrd>=1.0.0",
]
2 changes: 1 addition & 1 deletion src/mkdocs_table_reader_plugin/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.1.0"
__version__ = "4.0.0"
8 changes: 1 addition & 7 deletions src/mkdocs_table_reader_plugin/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,7 @@ def escape(value):
return value

df.columns = [escape(c) for c in df.columns]

# Avoid deprecated applymap warning on pandas>=2.0
# See https://github.com/timvink/mkdocs-table-reader-plugin/issues/55
if pd.__version__ >= "2.1.0":
df = df.map(escape)
else:
df = df.applymap(escape)
df = df.map(escape)

return df.to_markdown(**markdown_kwargs)

Expand Down
17 changes: 0 additions & 17 deletions src/mkdocs_table_reader_plugin/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import os
from inspect import signature


Expand All @@ -21,19 +20,3 @@ def kwargs_not_in_func(keywordargs, *funcs):
keywords = get_keywords(*funcs)
return {k: v for k, v in keywordargs.items() if k not in keywords}


class cd:
"""
Context manager for changing the current working directory
Credits: https://stackoverflow.com/a/13197763/5525118
"""

def __init__(self, newPath):
self.newPath = os.path.expanduser(newPath)

def __enter__(self):
self.savedPath = os.getcwd()
os.chdir(self.newPath)

def __exit__(self, etype, value, traceback):
os.chdir(self.savedPath)
5 changes: 5 additions & 0 deletions tests/fixtures/indentation/docs/basic_table.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"a","b"
40,73
50,52
531456,80
"name","table1"
20 changes: 20 additions & 0 deletions tests/fixtures/indentation/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Test page

Tables keep the indentation of their tag, so they can go inside components
that rely on indentation.

## Inside an admonition

!!! note "A note"

{{ read_csv("basic_table.csv") }}

## Inside a content tab

=== "A tab"

{{ read_csv("basic_table.csv") }}

## Without indentation

{{ read_csv("basic_table.csv") }}
13 changes: 13 additions & 0 deletions tests/fixtures/indentation/mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
site_name: test git_table_reader site
use_directory_urls: false

plugins:
- search
- table-reader:
data_path: "docs"

markdown_extensions:
- admonition
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
3 changes: 3 additions & 0 deletions tests/fixtures/raw_content/assets/tables/markdown_with_tag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This file documents a reader tag:

{{ read_csv("no_such_table.csv") }}
4 changes: 4 additions & 0 deletions tests/fixtures/raw_content/docs/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Test page

This is a table that we load from the docs folder, because we set `data_path` to `docs`:

## read raw content that itself contains a tag

{{ read_raw("assets/tables/markdown_with_tag.md") }}
5 changes: 5 additions & 0 deletions tests/fixtures/select_readers/docs/basic_table.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"a","b"
40,73
50,52
531456,80
"name","table1"
5 changes: 5 additions & 0 deletions tests/fixtures/select_readers/docs/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"columns":["col 1","col 2"],
"index":["row 1","row 2"],
"data":[["a","1234json"],["c","d"]]
}
11 changes: 11 additions & 0 deletions tests/fixtures/select_readers/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Test page

Only `read_csv` is selected in mkdocs.yml.

## A selected reader

{{ read_csv("basic_table.csv") }}

## A reader that is not selected

{{ read_json("data.json") }}
9 changes: 9 additions & 0 deletions tests/fixtures/select_readers/mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
site_name: test git_table_reader site
use_directory_urls: false

plugins:
- search
- table-reader:
data_path: "docs"
select_readers:
- read_csv
9 changes: 9 additions & 0 deletions tests/fixtures/select_readers/mkdocs_unknown_reader.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
site_name: test git_table_reader site
use_directory_urls: false

plugins:
- search
- table-reader:
data_path: "docs"
select_readers:
- read_avro
131 changes: 129 additions & 2 deletions tests/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
```
"""

import re
import logging
import os
import re
import shutil
import logging
import sys

import pandas as pd
import pytest
from click.testing import CliRunner
Expand Down Expand Up @@ -562,3 +563,129 @@ def test_backslashes_in_tables(tmp_path):
# values are inserted as they are, and not expanded as a regex replacement
assert r"C:\1 path" in contents
assert r"hi\nthere" in contents


def test_allow_missing_files(tmp_path):
"""
With 'allow_missing_files', a missing table is a warning instead of an error.
"""

tmp_proj = setup_clean_mkdocs_folder(
"tests/fixtures/wrongpath/mkdocs_allow_missing.yml", tmp_path
)

result = build_docs_setup(tmp_proj)
assert result.exit_code == 0, "'mkdocs build' command failed"

contents = (tmp_proj / "site/index.html").read_text()
# The tag is replaced with a note about the missing file
assert "{{ Cannot find 'non_existing_table.csv' }}" in contents
# And the tables that do exist are still inserted
assert re.search(r"531456", contents)


def test_select_readers(tmp_path):
"""
Only the selected readers replace their tag.
"""

tmp_proj = setup_clean_mkdocs_folder(
"tests/fixtures/select_readers/mkdocs.yml", tmp_path
)

result = build_docs_setup(tmp_proj)
assert result.exit_code == 0, "'mkdocs build' command failed"

contents = (tmp_proj / "site/index.html").read_text()
# read_csv() is selected, so its table is inserted
assert re.search(r"531456", contents)
# read_json() is not, so its tag is left alone
assert '{{ read_json("data.json") }}' in contents
assert "1234json" not in contents


def test_select_readers_unknown(tmp_path):
"""
A reader that does not exist is a configuration error.
"""

tmp_proj = setup_clean_mkdocs_folder(
"tests/fixtures/select_readers/mkdocs_unknown_reader.yml", tmp_path
)

result = build_docs_setup(tmp_proj)
assert result.exit_code == 1, "'mkdocs build' command should have failed"
assert "select_readers" in result.output
assert "read_avro" in result.output


def test_indentation(tmp_path):
"""
A table keeps the indentation of its tag, so it can go inside components
that rely on indentation, like admonitions and content tabs.
"""

tmp_proj = setup_clean_mkdocs_folder(
"tests/fixtures/indentation/mkdocs.yml", tmp_path
)

result = build_docs_setup(tmp_proj)
assert result.exit_code == 0, "'mkdocs build' command failed"

contents = (tmp_proj / "site/index.html").read_text()
# Every tag was replaced
assert "read_csv" not in contents
assert len(re.findall(r"<table>", contents)) == 3
# The indented tables ended up inside the component, instead of after it
assert re.search(
r'<div class="admonition note">\s*<p class="admonition-title">A note</p>\s*<table>',
contents,
), "the table was not inserted inside the admonition"
assert re.search(
r'<div class="tabbed-block">\s*<table>', contents
), "the table was not inserted inside the content tab"


def test_tags_in_inserted_content(tmp_path):
"""
Inserted content is not searched for tags itself.

Every tag is replaced in a single pass, so a table or a raw file that
contains something that looks like a tag is inserted as-is.
"""

tmp_proj = setup_clean_mkdocs_folder(
"tests/fixtures/raw_content/mkdocs.yml", tmp_path
)

result = build_docs_setup(tmp_proj)
assert result.exit_code == 0, "'mkdocs build' command failed"

contents = (tmp_proj / "site/index.html").read_text()
# The raw file was inserted
assert "This file documents a reader tag" in contents
# ..including the tag it contains, which was not read (the file does not exist)
assert '{{ read_csv("no_such_table.csv") }}' in contents


def test_malformed_tags(tmp_path):
"""
A tag that is not formatted correctly is left alone, instead of failing the build.
"""

tmp_proj = setup_clean_mkdocs_folder(
"tests/fixtures/basic_setup/mkdocs.yml", tmp_path
)

result = build_docs_setup(tmp_proj)
assert result.exit_code == 0, "'mkdocs build' command failed"

contents = (tmp_proj / "site/bad_tags.html").read_text()
for tag in [
"{{ read_csv }}", # no call
"{{ read_csv() }}", # no arguments
"{{read_csv('path')}}", # no spaces inside the braces
"{{read_csv('path') }}",
"{{ read_csv('path')}}",
]:
assert tag in contents, f"the malformed tag {tag} was not left alone"
38 changes: 38 additions & 0 deletions tests/test_markdown.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import pandas as pd
import pytest

from mkdocs_table_reader_plugin.markdown import (
add_indentation,
convert_to_md_table,
fix_indentation,
replace_newlines,
replace_unescaped_pipes,
)
Expand Down Expand Up @@ -57,3 +60,38 @@ def test_convert_to_md_table_multiline_other_tablefmt():
assert "<br>" not in md
assert "Sometimes the cell text is quoted." in md
assert "But not always" in md


def test_add_indentation():
"""
The filter used with mkdocs-macros-plugin, which strips indentation itself.
"""
table = "| a |\n|---|\n| 1 |"

# Surrounded by newlines, so the table starts on a line of its own
assert add_indentation(table) == f"\n{table}\n"
assert add_indentation(table, spaces=4) == "\n | a |\n |---|\n | 1 |\n"
assert add_indentation(table, tabs=1) == "\n\t| a |\n\t|---|\n\t| 1 |\n"

# Empty lines are left empty, instead of becoming trailing whitespace
assert add_indentation("a\n\nb", spaces=2) == "\n a\n\n b\n"


def test_add_indentation_spaces_and_tabs():
with pytest.raises(ValueError):
add_indentation("| a |", spaces=4, tabs=1)


def test_fix_indentation():
"""
The indentation of a tag is applied to the table that replaces it.
"""
table = "| a |\n|---|\n| 1 |"

assert fix_indentation(table, leading_spaces="") == table
assert fix_indentation(table, leading_spaces=" ") == " | a |\n |---|\n | 1 |"
assert fix_indentation(table, leading_spaces="\t") == table

# Rounded down to a multiple of 4 spaces, which is one markdown indentation level
assert fix_indentation(table, leading_spaces=" ") == table
assert fix_indentation(table, leading_spaces=" ") == fix_indentation(table, leading_spaces=" ")
Loading
Loading