Skip to content

Commit 2cdc786

Browse files
authored
Refactor for compatibility with macros
1 parent 74817da commit 2cdc786

File tree

11 files changed

+152
-54
lines changed

11 files changed

+152
-54
lines changed

mkdocs_table_reader_plugin/plugin.py

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,47 @@ def on_config(self, config, **kwargs):
3232
Returns:
3333
Config
3434
"""
35+
self.readers = {reader: READERS[reader].set_config_context(mkdocs_config=config, plugin_config=self.config) for reader in self.config.get('select_readers') if reader in self.config.get('select_readers',[])}
36+
3537
plugins = [p for p in config.get("plugins")]
3638

37-
for post_load_plugin in ["macros", "markdownextradata"]:
39+
# Plugins required before table-reader
40+
for post_load_plugin in ["markdownextradata"]:
3841
if post_load_plugin in plugins:
3942
if plugins.index("table-reader") > plugins.index(post_load_plugin):
4043
raise ConfigurationError(f"[table-reader]: Incompatible plugin order: Define 'table-reader' before '{post_load_plugin}' in your mkdocs.yml.")
44+
45+
# Plugins required after table-reader
46+
for post_load_plugin in ["macros"]:
47+
if post_load_plugin in plugins:
48+
if plugins.index("table-reader") < plugins.index(post_load_plugin):
49+
raise ConfigurationError(f"[table-reader]: Incompatible plugin order: Define 'table-reader' after '{post_load_plugin}' in your mkdocs.yml.")
50+
51+
if "macros" in config.plugins:
52+
config.plugins['macros'].macros.update(self.readers)
53+
config.plugins['macros'].variables['macros'].update(self.readers)
54+
config.plugins['macros'].env.globals.update(self.readers)
55+
self.external_jinja_engine = True
56+
else:
57+
self.external_jinja_engine = False
58+
59+
60+
def on_pre_page(self, page, config, **kwargs):
61+
"""
62+
See https://www.mkdocs.org/dev-guide/plugins/#on_pre_page.
63+
64+
Args:
65+
page: mkdocs.nav.Page instance
66+
config: global configuration object
4167
68+
Returns:
69+
Page
70+
"""
71+
# store the current page in the plugin config
72+
# because the readers have access to the plugin config, they can know where the current page is
73+
# this way, they can check that directory too
74+
self.config._current_page = page.file.abs_src_path
75+
return page
4276

4377
def on_page_markdown(self, markdown, page, config, files, **kwargs):
4478
"""
@@ -60,21 +94,11 @@ def on_page_markdown(self, markdown, page, config, files, **kwargs):
6094
Returns:
6195
str: Markdown source text of page as string
6296
"""
63-
# Determine the mkdocs directory
64-
# We do this during the on_page_markdown() event because other plugins
65-
# might have changed the directory.
66-
if self.config.get("base_path") == "config_dir":
67-
mkdocs_dir = os.path.dirname(os.path.abspath(config["config_file_path"]))
68-
if self.config.get("base_path") == "docs_dir":
69-
mkdocs_dir = os.path.abspath(config["docs_dir"])
70-
71-
# Define directories to search for tables
72-
search_directories = [os.path.join(mkdocs_dir, self.config.get("data_path"))]
73-
if self.config.get("search_page_directory"):
74-
search_directories.append(os.path.dirname(page.file.abs_src_path))
97+
if self.external_jinja_engine:
98+
return markdown
7599

76-
for reader in self.config.get('select_readers'):
77-
function = READERS[reader]
100+
for reader in self.readers:
101+
function = self.readers[reader]
78102
# Regex pattern for tags like {{ read_csv(..) }}
79103
# match group 0: to extract any leading whitespace
80104
# match group 1: to extract the arguments (positional and keywords)
@@ -92,36 +116,11 @@ def on_page_markdown(self, markdown, page, config, files, **kwargs):
92116

93117
# Safely parse the arguments
94118
pd_args, pd_kwargs = parse_argkwarg(result[1])
95-
96-
# Extract the filepath,
97-
# which is the first positional argument
98-
# or a named argument when there are no positional arguments
99-
if len(pd_args) > 0:
100-
input_file_path = pd_args.pop(0)
101-
else:
102-
input_file_path = pd_kwargs.pop("filepath_or_buffer")
103-
104-
# Validate if file exists
105-
search_file_paths = [os.path.join(search_dir, input_file_path) for search_dir in search_directories]
106-
valid_file_paths = [p for p in search_file_paths if os.path.exists(p)]
107-
if len(valid_file_paths) == 0:
108-
msg = f"[table-reader-plugin]: Cannot find table file '{input_file_path}'. The following directories were searched: {*search_directories,}"
109-
if self.config.get("allow_missing_files"):
110-
logger.warning(msg)
111-
112-
# Add message in markdown
113-
updated_tag = fix_indentation(leading_spaces, f"{{{{ Cannot find '{input_file_path}' }}}}")
114-
115-
markdown = tag_pattern.sub(updated_tag, markdown, count=1)
116-
117-
continue
118-
else:
119-
raise FileNotFoundError(msg)
120119

121120
# Load the table
122121
# note we use the first valid file paths,
123122
# where we first search the 'data_path' and then the page's directory.
124-
markdown_table = function(valid_file_paths[0], *pd_args, **pd_kwargs)
123+
markdown_table = function(*pd_args, **pd_kwargs)
125124
markdown_table = fix_indentation(leading_spaces, markdown_table)
126125

127126
# Insert markdown table

mkdocs_table_reader_plugin/readers.py

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,71 @@
11
import pandas as pd
22
import yaml
3+
import os
4+
from pathlib import Path
5+
import logging
6+
7+
import functools
38

49
from mkdocs_table_reader_plugin.utils import kwargs_in_func, kwargs_not_in_func
510
from mkdocs_table_reader_plugin.markdown import convert_to_md_table
611

7-
def read_csv(*args, **kwargs):
8-
12+
logger = logging.getLogger("mkdocs.plugins")
13+
14+
15+
class ParseArgs:
16+
def __init__(self, func):
17+
functools.update_wrapper(self, func)
18+
self.func = func
19+
self.mkdocs_config = None
20+
self.plugin_config = None
21+
22+
def set_config_context(self, mkdocs_config, plugin_config):
23+
self.mkdocs_config = mkdocs_config
24+
self.plugin_config = plugin_config
25+
return self
26+
27+
def __call__(self, *args, **kwargs):
28+
assert self.mkdocs_config is not None, "mkdocs_config is not set"
29+
assert self.plugin_config is not None, "plugin_config is not set"
30+
31+
# Extract the filepath,
32+
# which is the first positional argument
33+
# or a named argument when there are no positional arguments
34+
args = list(args)
35+
if len(args) > 0:
36+
input_file_name = args.pop(0)
37+
else:
38+
input_file_name = kwargs.pop("filepath_or_buffer")
39+
40+
possible_file_paths = [
41+
Path(os.path.dirname(os.path.abspath(self.mkdocs_config["config_file_path"]))) / Path(self.plugin_config.get("data_path")) / input_file_name,
42+
Path(os.path.abspath(self.mkdocs_config["docs_dir"])) / Path(self.plugin_config.get("data_path")) / input_file_name,
43+
Path(self.plugin_config._current_page).parent / input_file_name
44+
]
45+
valid_file_paths = [path for path in possible_file_paths if path.exists()]
46+
if len(valid_file_paths) == 0:
47+
msg = f"[table-reader-plugin]: Cannot find table file '{input_file_name}'. The following directories were searched: {*possible_file_paths,}"
48+
if self.plugin_config.get("allow_missing_files"):
49+
logger.warning(msg)
50+
return f"{{{{ Cannot find '{input_file_name}' }}}}"
51+
else:
52+
raise FileNotFoundError(msg)
53+
54+
return self.func(valid_file_paths[0], *args, **kwargs)
55+
56+
57+
58+
@ParseArgs
59+
def read_csv(*args, **kwargs) -> str:
960
read_kwargs = kwargs_in_func(kwargs, pd.read_csv)
1061
df = pd.read_csv(*args, **read_kwargs)
1162

1263
markdown_kwargs = kwargs_not_in_func(kwargs, pd.read_csv)
1364
return convert_to_md_table(df, markdown_kwargs)
1465

1566

16-
def read_table(*args, **kwargs):
67+
@ParseArgs
68+
def read_table(*args, **kwargs) -> str:
1769

1870
read_kwargs = kwargs_in_func(kwargs, pd.read_table)
1971
df = pd.read_table(*args, **read_kwargs)
@@ -22,30 +74,35 @@ def read_table(*args, **kwargs):
2274
return convert_to_md_table(df, markdown_kwargs)
2375

2476

25-
def read_fwf(*args, **kwargs):
77+
@ParseArgs
78+
def read_fwf(*args, **kwargs) -> str:
2679
read_kwargs = kwargs_in_func(kwargs, pd.read_fwf)
2780
df = pd.read_fwf(*args, **read_kwargs)
2881

2982
markdown_kwargs = kwargs_not_in_func(kwargs, pd.read_fwf)
3083
return convert_to_md_table(df, markdown_kwargs)
3184

32-
def read_json(*args, **kwargs):
85+
86+
@ParseArgs
87+
def read_json(*args, **kwargs) -> str:
3388
read_kwargs = kwargs_in_func(kwargs, pd.read_json)
3489
df = pd.read_json(*args, **read_kwargs)
3590

3691
markdown_kwargs = kwargs_not_in_func(kwargs, pd.read_json)
3792
return convert_to_md_table(df, markdown_kwargs)
3893

3994

40-
def read_excel(*args, **kwargs):
95+
@ParseArgs
96+
def read_excel(*args, **kwargs) -> str:
4197
read_kwargs = kwargs_in_func(kwargs, pd.read_excel)
4298
df = pd.read_excel(*args, **read_kwargs)
4399

44100
markdown_kwargs = kwargs_not_in_func(kwargs, pd.read_excel)
45101
return convert_to_md_table(df, markdown_kwargs)
46102

47103

48-
def read_yaml(*args, **kwargs):
104+
@ParseArgs
105+
def read_yaml(*args, **kwargs) -> str:
49106

50107
json_kwargs = kwargs_in_func(kwargs, pd.json_normalize)
51108
with open(args[0], "r") as f:
@@ -54,14 +111,18 @@ def read_yaml(*args, **kwargs):
54111
markdown_kwargs = kwargs_not_in_func(kwargs, pd.json_normalize)
55112
return convert_to_md_table(df, markdown_kwargs)
56113

57-
def read_feather(*args, **kwargs):
114+
115+
@ParseArgs
116+
def read_feather(*args, **kwargs) -> str:
58117
read_kwargs = kwargs_in_func(kwargs, pd.read_feather)
59118
df = pd.read_feather(*args, **read_kwargs)
60119

61120
markdown_kwargs = kwargs_not_in_func(kwargs, pd.read_feather)
62121
return convert_to_md_table(df, markdown_kwargs)
63122

64-
def read_raw(*args, **kwargs):
123+
124+
@ParseArgs
125+
def read_raw(*args, **kwargs) -> str:
65126
"""Read a file as-is.
66127
67128
Returns:

tests/fixtures/basic_setup/mkdocs_w_macros.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ use_directory_urls: true
33

44
plugins:
55
- search
6-
- table-reader
7-
- macros
6+
- macros
7+
- table-reader

tests/fixtures/basic_setup/mkdocs_w_macros_wrong_order.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ use_directory_urls: true
33

44
plugins:
55
- search
6-
- macros
7-
- table-reader
6+
- table-reader
7+
- macros
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"a","b"
2+
40,73
3+
50,52
4+
531456,80
5+
"name","table1"
File renamed without changes.

tests/fixtures/jinja/docs/index.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Test page
2+
3+
This is a table that we load from the docs folder, because we set `data_path` to `docs`:
4+
5+
## Dynamically insert a list of tables using jinja2
6+
7+
8+
{% set table_names = ["basic_table.csv","basic_table2.csv"] %}
9+
{% for table_name in table_names %}
10+
11+
### table `{{ table_name }}`
12+
13+
{{ read_csv(table_name) }}
14+
15+
{% endfor %}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"a","b"
2+
40,73
3+
50,52
4+
531456,80
5+
"name","table1"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# page
2+
3+
Test that searching in page directory works
4+
5+
{{ read_csv("sub_table.csv") }}

tests/fixtures/jinja/mkdocs.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
site_name: test git_table_reader site
2+
use_directory_urls: true
3+
4+
plugins:
5+
- search
6+
- macros
7+
- table-reader

0 commit comments

Comments
 (0)