mikitex70 / plantuml-markdown

PlantUML plugin for Python-Markdown
BSD 2-Clause "Simplified" License
192 stars 55 forks source link

Provide option to generate "Error images" on bad plantuml syntax instead of raising an error #61

Closed goldsam closed 2 years ago

goldsam commented 2 years ago

Summary

I propose an mode of operation where syntax errors (or any plantuml errors) are encoded within the generated image itself.

Context

I am trying to use plantuml-markdown as an mkdocs plugin while performing live-reload editing via mkdocs serve. The implementation of mkdocs serve aborts on any raised python error (PlantUMLHTTPError in my case). This forces the user to restart the mkdocs server resulting in a very unpleasant user experience.

While you could argue this is a problem with mkdocs and not this plugin, I would counter that exposing errors message in place of the generated image would produce a very nice user experience for many use cases. In documents containing many diagrams, this would allow the user to receive inline error feedback instead if out-of-band feedback via error message and stack trace.

As another example use case, consider a CI/CD development pipeline where it is undesirable to fail an entire documentation build due to one line of bad syntax.

The approach I am suggesting is used by vscode-markdown-preview-enhanced and produces a very nice user experience. Below is an example screenshot:

Example inline plantuml error!

mikitex70 commented 2 years ago

Hi @goldsam, the behavior should already be that. Example diagram with bad syntax:

```uml

component "a" #0000a0 as aaa

Rendered page:
![immagine](https://user-images.githubusercontent.com/2070527/155668241-03ba4a3b-a41e-4610-952d-3522ae048c6f.png)

and in the `mkdocs` logs:

INFO - [07:48:02] Detected file changes INFO - Building documentation... Error in "uml" directive: b'ERROR\n1\nSyntax Error?\nSome diagram description contains errors\n' INFO - [07:48:19] Reloading browsers INFO - [07:48:19] Browser connected: http://localhost:8000/


The `mkdocs` does not restart, it simply detects page changes, logs an error and continues to run.

Can you give me more details to be able to reproduce your issue? What mkdocs and plantuml-markdown plugin version are you using? Can you attach a simple diagram with an error so I can do some test?
goldsam commented 2 years ago

Thanks for the quick response @mikitex70 !

I'm using the following versions:

You can reproduce the issue using the actual container project i'm working on. For your convenience, the whole thing can be cloned, built and run using:

git clone https://github.com/goldsam/mkdocs.git goldsam-mkdocs-mre
cd goldsam-mkdocs-mre
dos2unix resources/entrypoint.sh
docker build . -t mkdocs
MSYS_NO_PATHCONV=1 docker run --rm -v $(pwd)/demo-project:/docs -p 8000:8000 mkdocs serve

It should be noted that this container instantiates a local plantuml server which is referenced by my mkdocs config via custom PLANTUML_SERVER env-var defined in my Dockerfile.

Once the mkdocs server is up, try to save an invalid change to demo-project\docs\index.md. For me, mkdocs immediately crashes with a stack trace:

waiting for plauntuml server to be ready.webPort=8001
.plantuml server is ready.INFO     -  Building documentation...
WARNING  -  Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0' suggests a production environment or the use of a proxy to connect to the MkDocs server. However, the MkDocs' server is intended for local development purposes only. Please use a third party production-ready server instead.
INFO     -  Cleaning site directory
INFO     -  Documentation built in 0.55 seconds
INFO     -  [15:37:42] Serving on http://0.0.0.0:8000/
INFO     -  [15:37:43] Browser connected: http://localhost:8000/
INFO     -  [15:37:43] Browser connected: http://localhost:8000/
INFO     -  [15:47:30] Detected file changes
INFO     -  Building documentation...
WARNING  -  Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0' suggests a production environment or the use of a proxy to connect to the MkDocs server. However, the MkDocs' server is intended for local development purposes only. Please use a third party production-ready server instead.
ERROR    -  Error reading page 'index.md': 'PlantUMLHTTPError' object has no attribute 'message'
Traceback (most recent call last):
  File "/usr/bin/mkdocs", line 8, in <module>
    sys.exit(cli())
  File "/usr/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3.9/site-packages/click/core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3.9/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/mkdocs/__main__.py", line 177, in serve_command
    serve.serve(dev_addr=dev_addr, livereload=livereload, **kwargs)
  File "/usr/lib/python3.9/site-packages/mkdocs/commands/serve.py", line 81, in serve
    server.serve()
  File "/usr/lib/python3.9/site-packages/mkdocs/livereload/__init__.py", line 99, in serve
    self._build_loop()
  File "/usr/lib/python3.9/site-packages/mkdocs/livereload/__init__.py", line 121, in _build_loop
    func()
  File "/usr/lib/python3.9/site-packages/mkdocs/commands/serve.py", line 49, in builder
    build(config, live_server=live_server, dirty=dirty)
  File "/usr/lib/python3.9/site-packages/mkdocs/commands/build.py", line 292, in build
    _populate_page(file.page, config, files, dirty)
  File "/usr/lib/python3.9/site-packages/mkdocs/commands/build.py", line 174, in _populate_page
    page.render(config, files)
  File "/usr/lib/python3.9/site-packages/mkdocs/structure/pages.py", line 174, in render
    self.content = md.convert(self.markdown)
  File "/usr/lib/python3.9/site-packages/markdown/core.py", line 261, in convert
    self.lines = prep.run(self.lines)
  File "/usr/lib/python3.9/site-packages/plantuml_markdown.py", line 125, in run
    text1, idx1 = self._replace_block(text[idx:])
  File "/usr/lib/python3.9/site-packages/plantuml_markdown.py", line 183, in _replace_block
    diagram = self._render_diagram(code, requested_format)
  File "/usr/lib/python3.9/site-packages/plantuml_markdown.py", line 268, in _render_diagram
    diagram = self._render_remote_uml_image(code, requested_format)
  File "/usr/lib/python3.9/site-packages/plantuml_markdown.py", line 317, in _render_remote_uml_image
    return PlantUML("%s/%s/" % (self.config['server'], img_format)).processes(plantuml_code)
  File "/usr/lib/python3.9/site-packages/plantuml.py", line 173, in processes
    raise PlantUMLHTTPError(response, content)
  File "/usr/lib/python3.9/site-packages/plantuml.py", line 56, in __init__
    if not self.message:
AttributeError: 'PlantUMLHTTPError' object has no attribute 'message'
mikitex70 commented 2 years ago

Hi @goldsam, I was able to reproduce your issue and to fix it. The problem was in the plantuml package, which raises an exception if the remote server respond with an HTTP 400. I was able to do a workaround and now it should work as expected; try version 3.5.2. Thanks for reporting it.

goldsam commented 2 years ago

Thank you so much @mikitex70! 😊