Skip to content

Commit

Permalink
Merge branch 'release/3.10.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mikitex70 committed Aug 1, 2024
2 parents 2894767 + ffa33d0 commit 5e5097d
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 31 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Changelog


## 3.10.0 (2024-08-01)

### Fix

* Fixed inclusion from `mkdocs-multirepo-plugin` (refs #100) [Michele Tessaro]

Expanded the `base_dir` configuration to handle a list of paths where to
search for files to include.


## 3.9.8 (2024-07-13)

### New
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ plantuml_markdown:
# other global options
insecure: False # set to True if the server uses self-signed certificates
cachedir: /tmp # set a non-empty value to enable caching
base_dir: . # where to search for diagrams to include
base_dir: . # where to search for diagrams to include (can be a list)
config: # PlantUML config file, relative to base_dir (a PlantUML file included in every diagram)
format: png # default diagram image format
classes: class1,class2 # default diagram classes
Expand Down Expand Up @@ -235,7 +235,7 @@ Plugin options
The plugin has several configuration option:
* `alt`: text to show when image is not available. Defaults to `uml diagram`
* `base_dir`: path where to search for external diagrams files
* `base_dir`: path where to search for external diagrams files. Defaults to `.`, can be a list of paths
* `cachedir`: directory for caching of diagrams. Defaults to `''`, no caching
* `classes`: space separated list of classes for the generated image. Defaults to `uml`
* `config`: PlantUML config file, relative to `base_dir` (a PlantUML file included before every diagram, see
Expand Down
21 changes: 12 additions & 9 deletions plantuml_markdown.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
"base_dir": {
"title": "Path where to search for external diagrams files",
"markdownDescription": "https://github.com/mikitex70/plantuml-markdown#plugin-options",
"type": "string"
"type": ["string", "array"],
"items": {
"type": "string"
}
},
"cachedir": {
"title": "Directory for caching diagrams to speed up subsequent builds. Defaults to `''`, no caching",
Expand Down Expand Up @@ -120,12 +123,12 @@
}
}
],
"definitions": {
"format_options": {
"enum": [ "png", "svg", "svg_object", "svg_inline", "txt" ]
},
"http_methods": {
"enum": [ "GET", "POST" ]
}
}
"definitions": {
"format_options": {
"enum": [ "png", "svg", "svg_object", "svg_inline", "txt" ]
},
"http_methods": {
"enum": [ "GET", "POST" ]
}
}
}
104 changes: 85 additions & 19 deletions plantuml_markdown/plantuml_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def __init__(self, md):
self._cachedir: Optional[str] = None
self._plantuml_server: Optional[str] = None
self._kroki_server: Optional[str] = None
self._base_dir: Optional[str] = None
self._base_dir: Optional[str | List[str]] = None
self._encoding: str = 'utf-8'
self._http_method: str = 'GET'
self._fallback_to_get: bool = True
Expand All @@ -141,11 +141,14 @@ def run(self, lines: List[str]) -> List[str]:
self._cachedir = self.config['cachedir']
self._plantuml_server = self.config['server']
self._kroki_server = self.config['kroki_server']
self._base_dir = self.config['base_dir']
self._encoding = self.config['encoding'] or self._encoding
self._http_method = self.config['http_method'].strip()
self._fallback_to_get = bool(self.config['fallback_to_get'])
self._config_path = self.config['config']
self._base_dir = self.config['base_dir']

if isinstance(self._base_dir, str):
self._base_dir = [self._base_dir]

text = '\n'.join(lines)
idx = 0
Expand Down Expand Up @@ -210,8 +213,17 @@ def _replace_block(self, text: str) -> Tuple[str, int]:
code = ""
# Add external diagram source.
if source and self._base_dir:
with open(os.path.join(self._base_dir, source), 'r', encoding=self._encoding) as f:
code += f.read()
for base_dir in self._base_dir:
source = os.path.join(base_dir, source)

if os.path.exists(source):
with open(source, 'r', encoding=self._encoding) as f:
code += f.read()
break
else:
diag_tag = self._render_error('Cannot find external diagram source: ' + source)
return (text[:m.start()] + m.group('indent') + diag_tag + text[m.end():], \
m.start() + len(m.group('indent')) + len(diag_tag))
# Add extracted markdown diagram text.
code += m.group('code')

Expand Down Expand Up @@ -532,15 +544,37 @@ def __init__(self, lang: str, kroki: bool, white_lists: List[str], dark_mode: bo
self._diagram_type = 'uml'

# Given a PlantUML source, replace any "!include" directive with the included code, recursively
def readFile(self, plantuml_code: str, directory: str) -> str:
def readFile(self, plantuml_code: str, directory: List[str]) -> str:
"""
Reads a PlantUML code and replaces any "!include" directives with the included code, recursively.
Args:
plantuml_code (str): The PlantUML code to process.
directory (List[str]): A list of directories to search for included files.
Returns:
str: The processed PlantUML code with all "!include" directives replaced.
"""
lines = plantuml_code.splitlines()
# Wrap the whole combined text between startuml and enduml tags as recursive processing would have removed them
# This is necessary for it to work correctly with plamtuml POST processing
all_lines = self._readFileRec(lines, directory)
return "@start"+self._diagram_type+"\n" + "\n".join(all_lines) + "\n@end"+self._diagram_type+"\n"

# Reads the file recursively
def _readFileRec(self, lines: List[str], directory: str) -> List[str]:
def _readFileRec(self, lines: List[str], search_dirs: List[str]) -> List[str]:
"""
Recursively reads a list of lines and replaces any "!include" directives with the included code.
Args:
lines (List[str]): The list of lines to process.
search_dirs (List[str]): A list of directories to search for included files.
Returns:
List[str]: The processed list of lines with all "!include" directives replaced.
"""
result: List[str] = []

for line in lines:
Expand All @@ -558,7 +592,7 @@ def _readFileRec(self, lines: List[str], directory: str) -> List[str]:
self._definitions[match.group('varname')] = match.group('value')
result.append(line_striped)
elif line_striped.startswith("!include"):
result.append(self._readInclLine(line_striped, directory).strip())
result.append(self._readInclLine(line_striped, search_dirs).strip())
elif line_striped.startswith("@start"):
# remove startuml as plantuml POST method doesn't like it in include files
# we will wrap the whole combined text between start and end tags at the end
Expand All @@ -573,7 +607,19 @@ def _readFileRec(self, lines: List[str], directory: str) -> List[str]:

return result

def _readInclLine(self, line: str, directory: str) -> str:
def _readInclLine(self, line: str, search_dirs: List[str]) -> str:
"""
Reads the contents of an included file and returns its contents.
Args:
line (str): The line containing the !include directive.
search_dirs (List[str]): A list of directories to search for the included file.
Returns:
str: The processed line containing the included file contents, or the original line if the inclusion is
handled by the server.
"""
# If includeurl is found, we do not have to do anything here. Server can handle that (if enabled)
if "!includeurl" in line:
return line
Expand Down Expand Up @@ -611,16 +657,36 @@ def _readInclLine(self, line: str, directory: str) -> str:
return line # inclusion handled by the server
else:
# Read contents of the included file
return self._load_file(os.path.normpath(os.path.join(directory, inc_file)))

def _load_file(self, inc_file_abs: str):
try:
with open(inc_file_abs, "r") as inc:
return "\n".join(
self._readFileRec(inc.readlines(), os.path.dirname(os.path.realpath(inc_file_abs))))
except Exception as exc:
logger.error("Could not find include " + str(exc))
raise exc
return self._load_file(search_dirs, inc_file)

def _load_file(self, search_dirs: List[str], inc_file_abs: str):
"""
Loads a file from a list of search directories.
Args:
search_dirs (List[str]): A list of directories to search for the file.
inc_file_abs (str): The absolute path of the file to load.
Returns:
str: The contents of the loaded file.
Raises:
FileNotFoundError: If the file is not found in any of the search directories.
Exception: If an error occurs while loading the file.
"""
for inc_dir in search_dirs:
inc_file_abs = os.path.normpath(os.path.join(inc_dir, inc_file_abs))
if os.path.exists(inc_file_abs):
try:
with open(inc_file_abs, "r") as inc:
include_dirs = [os.path.dirname(os.path.realpath(inc_file_abs))]
include_dirs.extend(search_dirs)
return "\n".join(self._readFileRec(inc.readlines(), include_dirs))
except Exception as exc:
logger.error("Could not find include " + str(exc))
raise exc
else:
raise FileNotFoundError("Could not find include " + inc_file_abs)


# For details see https://python-markdown.github.io/extensions/api/#extendmarkdown
Expand All @@ -647,7 +713,7 @@ def __init__(self, **kwargs):
"Defaults to true"],
'priority': ["30", "Extension priority. Higher values means the extension is applied sooner than others. "
"Defaults to 30"],
'base_dir': [".", "Base directory for external files inclusion"],
'base_dir': [".", "Base directory for external files inclusion. Defaults to '.', can be a list of paths."],
'encoding': ["utf8", "Default character encoding for external files (default: utf8)"],
'http_method': ["GET", "Http Method for server - GET or POST", "Defaults to GET"],
'fallback_to_get': [True, "Fallback to GET if POST fails", "Defaults to True"],
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

setuptools.setup(
name="plantuml-markdown",
version="3.9.8",
version="3.10.0",
author="Michele Tessaro",
author_email="[email protected]",
description="A PlantUML plugin for Markdown",
Expand Down

0 comments on commit 5e5097d

Please sign in to comment.