RESPOND with a special char?

Hello all
is there any way to return to standard output a string containing “#” and “*” characters using RESPOND (ora any other method) ?
Need to interact with some custom scripts … but I’m stuck.
Thank you!

"!@#$%^&*&*("
M118 "!@#$%^&*&*("
"!!MSG"
M118 "!!MSG"

Try M118

Won’t work.
If you put the following line into a macro:

M118 “!@#$%^&&(”

will print

Recv:11:44:24.341: "!@

Yeah I don’t think it’s going to be possible, at least not with # as # is the indicator for a comment.

It would then try to read comments as GCode commands and break things.

Not sure where * plays into things but it’s a special character so would most likely break something.

“*” is the jinja2 character for multiplication

; is off the table too as it’s also a comment indicator, but it looks like @$%^& are good

[gcode_macro TellMeAStory]
gcode:
   M118 "@@ One upon a time"
   M118 "@@@$%^& There was a guy trying a macro"
   M118 "@@@$%^& To see if it would print special characters"
6:10 AM
"@@@$%^& To see if it would print special characters"
6:10 AM
"@@@$%^& There was a guy trying a macro"
6:10 AM
"@@ One upon a time"
1 Like

OK, therefore # is the kryptonite for Jinja.
THanks anyway!

This has nothing to do with Jinja, it’s part of config file processing. One workaround would, in fact, be to use Jinja to represent the character: { '\u0023' }

2 Likes

I only assumed Jinja because the Jinja is IN the config files. If it parsed it as anything but the expected value then it could cause issues.

It’s excluded in the regex specifically.

    # Parameter parsing helpers
    extended_r = re.compile(
        r'^\s*(?:N[0-9]+\s*)?'
        r'(?P<cmd>[a-zA-Z_][a-zA-Z0-9_]+)(?:\s+|$)'
        r'(?P<args>[^#*;]*?)'
        r'\s*(?:[#*;].*)?$')

That regex also has nothing to do with Jinja. Jinja is a templating language used within specific delimiters (e.g. { coordinate[1] + 0.25 * wipe } or {% set wipe_count = 8 %}), not a general term for all parsing done by Klipper. Config parsing happens well before any Jinja evaluation, while argument parsing happens after templating has taken place.

Thank you for clarification!
Therefore I need to rewrite above statement as

“#” is the kryptonite for Klipper macros :slight_smile:

Again, no. We’ve identified two areas where it’s potentially a problem: config file parsing and argument parsing for extended commands. “Macros” is not one of those areas, even though both are relevant to whether/how you can use some characters in macros.

[gcode_macro TEST_SPECIAL_CHARS]
description: Testing a thing
gcode:
    M118 This macro returns { '\u0023' } * { '\u0023' }; how interesting
klipper@cetus:~$ klipper-repl /run/klipper/api
06:40:17 PM  ## Connected to Klipper at cetus:/run/klipper/api
                ^C or ^D to quit; type M112 for emergency stop
cetus:/run/klipper/api* TEST_SPECIAL_CHARS
06:40:24 PM  echo: This macro returns # * #; how interesting

Klipper strips anything after a # as a comment when parsing lines.
This includes the GCode macros in config files.

See below

    def _parse_config(self, data, filename, fileconfig, visited):
        path = os.path.abspath(filename)
        if path in visited:
            raise error("Recursive include of config file '%s'" % (filename))
        visited.add(path)
        lines = data.split('\n')
        # Buffer lines between includes and parse as a unit so that overrides
        # in includes apply linearly as they do within a single file
        buffer = []
        for line in lines:
            # Strip trailing comment
            pos = line.find('#')    <----- HERE
            if pos >= 0:
                line = line[:pos]   <------ HERE
            # Process include or buffer line
            mo = configparser.RawConfigParser.SECTCRE.match(line)
            header = mo and mo.group('header')
            if header and header.startswith('include '):
                self._parse_config_buffer(buffer, filename, fileconfig)
                include_spec = header[8:].strip()
                self._resolve_include(filename, include_spec, fileconfig,
                                      visited)
            else:
                buffer.append(line)
        self._parse_config_buffer(buffer, filename, fileconfig)
        visited.remove(path)
2 Likes

I stand corrected, then.
Just drop a “#” and the rest of that line will be merrily ignored.
The only way to use it is to fool the parser with a hex representation, as Flowerysong correctly suggests.

The anarchist in me wonders if this could be exploited somehow. Sneak a # into a macro in a spot and then post a “Here’s a macro that does so and so” for unsuspecting users.

Not that I’m malicious enough to try to destroy someone’s printer, but I am dumb enough to see what I can break in order to make things better in the long run.

1 Like