Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Escaping text #17

Closed
pawamoy opened this issue Apr 26, 2021 · 5 comments
Closed

Escaping text #17

pawamoy opened this issue Apr 26, 2021 · 5 comments

Comments

@pawamoy
Copy link

pawamoy commented Apr 26, 2021

Hello! Thanks for ansimarkup, it's great and I love it 🙂

I just encountered an issue though, where the text I'm printing through the ansiprint function contains text such as <l type="V">2.0</l> (XML) which triggers a MismatchedTag: closing tag "</l>" has no corresponding opening tag.

I read the README and searched the issues, but I didn't find anything about this particular issue.
So, I'm kinda opening a feature request I guess?


I would like to be able to "escape" parts of the text to be printed.

I see two methods, providing an escape function or class that would:

  • wrap the text in special markers, telling the rest of the code not to interprete any tag in this wrapped text
  • replace < and > with <lt> and <gt> (or similar) that would then be re-parsed/printed as < as >

Here's how I would apply this new feature on my code:

 ansiprint(
     template.render(
         {
             "title": title,
-            "command": command,
-            "output": output,
+            "command": escape(command),
+            "output": escape(output),
         },
     ),
 )

I'm willing to try and send a PR if this is something you'd consider having 🙂

@pawamoy
Copy link
Author

pawamoy commented Feb 14, 2022

Hello, gentle ping 🙂

@gvalkov
Copy link
Owner

gvalkov commented Aug 9, 2023

Hi @pawamoy. This one really slipped though — sorry for that. I've implemented raw string handling in release 2.0.0. They work like this:

>>> from ansimarkup import ansiprint, parse, raw
>>> ansiprint("<b><r>", raw("<l type='V'>2.0</l>"), "</r></b>")
 <l type='V'>2.0</l>  # printed in bold red (note the leading space caused)

>>> s = parse("<b><r>", raw("<l type='V'>2.0</l>"), "</r></b>")
>>> print(s)
<l type='V'>2.0</l>

Another idea was to have something like an <ESC><l type='V'>2.0</l></ESC> , but I decided against it for now. Frankly, I think the template string approach is best (and was available all along):

>>> from ansimarkup import parse
>>> s = parse("<b><r>%s</r></b>")
>>> print(s % "<l type='V'>2.0</l>")
<l type='V'>2.0</l> 

@gvalkov gvalkov closed this as completed Aug 9, 2023
@pawamoy
Copy link
Author

pawamoy commented Aug 17, 2023

Hey @gvalkov, that's amazing, thanks! I'll try this soon and report back 🙂

@pawamoy
Copy link
Author

pawamoy commented Sep 18, 2023

Unfortunately this does not work for my use case. Users can provide their own templates, and my own code is responsible for escaping parts of the variables that get rendered by this template. I need a text-only solution while the current implementation relies on Python and isinstance checks.

I cannot use temporary placeholders either, as the actual values must be available for correct rendering. For example output gets indented by two spaces. If I replace it by a placeholder before rendering + ansprint, and replace it back after, it will have no indentation. In short the Jinja features would be rendered ineffective.

I thought about escaping the text myself, and then unescaping it after, but it won't work either, because by the time everything is rendered and ansi-printed, I cannot distinguish which parts should be unescaped anymore.

So IMO, for my use-case there's no other solution than ansiprint dealing with escaping itself, with something like <ESC>, as you mentioned 😕

EDIT: unless I use markup that has very small chances of appearing elsewhere to escape parts of the rendered text. I'll try that and report back.

@pawamoy
Copy link
Author

pawamoy commented Sep 18, 2023

Yep, I was able to make it work 🙂 Could have done that earlier haha.

from ansimarkup import parse

LT = "#FAILPRINT_LT#"
GT = "#FAILPRINT_GT#"

def escape(text: str) -> str:
    return text.replace("<", LT).replace(">", GT)

def unescape(text: str) -> str:
    return text.replace(LT, "<").replace(GT, ">")

template = (
    "{% if success %}<green>✓</green>"
    "{% elif nofail %}<yellow>✗</yellow>"
    "{% else %}<red>✗</red>{% endif %} "
    "<bold>{{ title or command|e }}</bold>"
    "{% if failure %} ({{ code }}){% endif %}"
    "{% if failure and output and not quiet %}\n"
    "{{ ('  > ' + command|e + '\n') if title and command else '' }}"
    "{{ output|indent(2 * ' ')|e }}{% endif %}"
)

env = Env(...)
env.filters["e"] = escape
rendered = env.from_string(template).render(...)
print(unescape(parse(rendered)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants