Replacing one string inside another but without interfering with a second replace

Moving .join outside the loop means it won’t run on each iteration hence nullifying the speed test.

re.compile: no not tried it, just doing so now…
Tried it but only reduces the time to 10 secs,

Yep but later on everything is processed through an F-string when the '{{' / '}}' will be converted back to '{' / '}' again

That is certainly quicker, not so easy to understand but definitely 30% faster. It seems to be writing the hex characters into the string which is mainly responsible for the speed difference.

For me that gets it down to 8 secs, still quite a bit slower than the replace.

No but it does prove that in this case the extra complexity of using RegEx over replace is not worth it.

Yep, I did that just for speed test purposes.

Just had a go at doing it by counting through the string one character at a time; took 1min 15secs, so that way is out.

Both using sentinal character (not pattern) with the }} → sentinal ; }}} ; sentinal → } replacement pattern and the }}} ; }}}}} replacement pattern are fine. (You can also write }}} → `` instead. The result is equivalent provided no }}} occurs in the original string.)

Looking it at like that, it’s clear the latter is probably slightly more efficient, because it only needs 2 passes.

x = x.replace("{", "{{").replace("{{{{", "{").replace("}", "}}").replace("}}}}", "}")

likely is the best python solution.[1]

If you want something much faster than this, you need to do something like writing a custom Rust library (where you can do a nice match-case iterating over the pairwise characters). But I can’t think of a single scenario where the development time required to create and plug in a rust library is worth >>being able to do this replacement pattern<< up to 4x faster.


  1. No disrespect intended to x = x.replace("{", "{{").replace("{{{", "").replace("}", "}}").replace("}}}", ""), which does feel cleverer ↩︎

Yes like it very much, cleaner to read. However you have introduced a very real question could ‘{{{{’ or ‘}}}}’ ever exist in JavaScript code, I suspect they might.

It’s absolutely possible. Deeply nested code, particularly if minified, can easily contain large numbers of consecutive open or close braces.

Hmm? What “hex characters”?

That looks nice, though I’d still write a loop to avoid conflict with a used character.

The private area characters are very very very unlikely to be in your data. Unless you are a processing data that include characters not standardised by the unicode committee.

I use the private area code points in my apps to solve the conflict issue and so far not seen an issue doing this.

I found this Rust library which MatchKind::LeftmostFirst behavior seems to be a solution:

In particular this code example proves it

There’s a Python binding on its lib.rs page, but unfortunately it doesn’t bind the replacing methods yet. I might do it if I have time in the future.

This causes a bit of a show-stopper problem then. For standardisation I had hoped to use the same {{ / }} delimiters as Jinja and Django but doing so would mean parsing the whole input string for tags which could potentially open up a whole new can of worms. I wonder how Jinja deals with this problem?

Seems, bearing in mind this conflict, that the use of curly brackets as delimiters is not a great idea.

If it’s easy to type, it has already been used and will potentially run into conflicts.

If it’s hard to type, it’s frustrating to use.

People create jinja templates knowing that they are creating jinja templates. there is no attempt to “parse all possible html templates”.
And if you’re writing a jinja template, you don’t write for example[1]

<script>
  {{console.log("Hello, World! 🌎");}}
</script>

You’re always able to unwrap your brackets like

<script>
  {
    {console.log("Hello, World! 🌎");}
  }
</script>

There is a lesson here for you too. You are (hopefully) cooperating with the template writers.


  1. (I do know nesting brackets like this is useless, but it’s valid JavaScript) ↩︎

Hmmm.. maybe I could think about tracking <script> tags then. However are there any other ways that JavaScript could be used in a HTML file without being within <script> tags?

Yes. HTML elements can have attributes that have JS. It’s not recommended any more but it is legal.

The perfect is the enemy of the good. Don’t try to make something that absolutely cannot possibly trip on legal code, and instead try to make something that’s useful. As @peterc said, you should be in a cooperative relationship with template writer(s) - probably mostly yourself - so pick something and just make sure that it’s possible to find alternatives (as is the case with a double brace, though notably NOT the case with a single brace).

Of course, there is no point checking if we are in a script tag or not as we could well be using a {pythonExpression} in there anyway.