Repository: ultrabug/py3status

Issue:
`google_calendar` shows `&lt;` instead of "<" in `{summary}` 
**Describe the bug**
Using `google_calendar` with an event title that has a "<" in it, it shows as `&lt;`

**Your py3status version**
py3status version 3.44 (python 3.10.5) on i3

**To Reproduce**
1. Add module `google_calendar`
2. Configure `google_calendar`'s `{format_event}` to include the event title (`{summary}`)
3. Have a google event that includes "<"
4. py3status shows `&lt;` instead of the less than sign

**Expected behavior**
py3status should show the less than sign.

**Screenshots**
This is how the event looks on the google calendar website:
![image](https://user-images.githubusercontent.com/17759291/174604335-1fa74060-92d2-4e9a-9860-4b8e363ae891.png)

This is how it shows up in py3status:
![image](https://user-images.githubusercontent.com/17759291/174604138-b425537f-26fd-44aa-878a-473b5ce8b9f0.png)


**Additional context**
This is my configuration for `google_calendar`:
```python
google_calendar {
    time_to_max = 99999
    events_within_hours = 48
    format_event = "[\?if=!is_current [\?color=lime {summary}][ {format_timer}]]"
    format_timer = "\?color=time (in [\?if=days {days}d ][\?if=hours {hours}h ][\?if=minutes {minutes}m])"
}
```


Discussion:
I have tested that https://github.com/ultrabug/py3status/commit/f6da6b8acc141011717a7c24c1f90b12352dce8a does introduce this bug. The commit before that works fine (it also had something to do with HTML formatting)
Does it work fine if you add `markup = "pango"` to your `google_calendar` config? 
Yes, that helps. Thanks! I wasn't aware of the `markup` setting.
I'm not sure if `markup = "pango"` is just a workaround or if my problem is an actual bug. Feel free to close this issue if it's not a bug.
I'll have to check if somehow the module could detect pango markup config and escape html only when pango is used
Here's an idea... For starters, users could manually specify which placeholders to escape html.
```diff
diff --git a/py3status/formatter.py b/py3status/formatter.py
index 9b8659a..e37ea0f 100644
--- a/py3status/formatter.py
+++ b/py3status/formatter.py
@@ -2,6 +2,7 @@ import re
 from math import ceil
 from numbers import Number
 from urllib.parse import parse_qsl
+from html import escape
 
 from py3status.composite import Composite
 from py3status.constants import COLOR_NAMES, COLOR_NAMES_EXCLUDED
@@ -322,6 +323,8 @@ class Placeholder:
                 # no remaining digits following it.  If the parameter cannot
                 # be successfully converted then the format will be removed.
                 try:
+                    if "escape" in self.format:
+                        value = escape(value)
                     if "ceil" in self.format:
                         value = ceil(float(value))
                     if "f" in self.format:
diff --git a/py3status/modules/static_string.py b/py3status/modules/static_string.py
index 739f7fb..bb3289f 100644
--- a/py3status/modules/static_string.py
+++ b/py3status/modules/static_string.py
@@ -16,12 +16,16 @@ class Py3status:
     """
 
     # available configuration parameters
-    format = "Hello, world!"
+    format = " \| ".join(["{ily}", "{ily2}", "{ily:escape}", "{ily2:escape}"])
 
     def static_string(self):
+        data = {
+            "ily": "I <3 U",
+            "ily2": "I <3 U TOO",
+        }
         return {
             "cached_until": self.py3.CACHE_FOREVER,
-            "full_text": self.py3.safe_format(self.format),
+            "full_text": self.py3.safe_format(self.format, data),
         }
 
 

```
```shell
$ python static_string.py --term
I <3 U | I <3 U TOO | I &lt;3 U | I &lt;3 U TOO
```

Resolving patch:
diff --git a/py3status/formatter.py b/py3status/formatter.py
--- a/py3status/formatter.py
+++ b/py3status/formatter.py
@@ -1,4 +1,5 @@
 import re
+from html import escape
 from math import ceil
 from numbers import Number
 from urllib.parse import parse_qsl
@@ -322,6 +323,8 @@ def get(self, get_params, block):
                 # no remaining digits following it.  If the parameter cannot
                 # be successfully converted then the format will be removed.
                 try:
+                    if "escape" in self.format:
+                        value = escape(value)
                     if "ceil" in self.format:
                         value = ceil(float(value))
                     if "f" in self.format:
diff --git a/py3status/modules/google_calendar.py b/py3status/modules/google_calendar.py
--- a/py3status/modules/google_calendar.py
+++ b/py3status/modules/google_calendar.py
@@ -154,7 +154,6 @@
 import datetime
 import time
 from pathlib import Path
-from html import escape
 
 try:
     from googleapiclient import discovery
@@ -411,7 +410,7 @@ def _build_response(self):
 
             event_dict = {}
 
-            event_dict["summary"] = escape(event.get("summary"))
+            event_dict["summary"] = event.get("summary")
             event_dict["location"] = event.get("location")
             event_dict["description"] = event.get("description")
             self.event_urls.append(


Test patch: