Fix: Unicode encoding and logging errors in lftools 47/73947/2 v0.35.17 v0.37.17
authorAnil Belur <abelur@linuxfoundation.org>
Fri, 5 Dec 2025 03:26:54 +0000 (13:26 +1000)
committerAnil Belur <abelur@linuxfoundation.org>
Fri, 5 Dec 2025 11:25:48 +0000 (21:25 +1000)
1. Fix UnicodeEncodeError in Gerrit/Nexus API requests:
   - Encode JSON string data as UTF-8 in RestApi._request()
   - Change Nexus encoding from latin-1 to utf-8 (5 locations)
   - Handles Unicode characters in responses (e.g., user names with 'š')
   - Fixes: 'latin-1' codec can't encode character '\u0161'

2. Fix logging format errors (3 locations):
   - GitHub create-repo: Use f-string instead of comma in log.info()
   - GitHub votes: Use f-string for approval list logging
   - Gerrit vote_on_change: Use f-string with parameters
   - Fixes: TypeError: not all arguments converted during string formatting

Based on: https://github.com/modeseven-lfit/lftools-uv/pull/53
Tested with: lftools gerrit addgithubrights git.opendaylight.org gnmi

Change-Id: Ief4a4ffa9bcc1d0a31342ac49b448fd097dda879
Signed-off-by: Anil Belur <abelur@linuxfoundation.org>
lftools/api/client.py
lftools/api/endpoints/gerrit.py
lftools/cli/github_cli.py
lftools/nexus/__init__.py
releasenotes/notes/fix-unicode-encoding-logging-c891ffd99f38c7f5.yaml [new file with mode: 0644]

index 612ce4d..9652746 100644 (file)
@@ -48,6 +48,10 @@ class RestApi(object):
         self, url: str, method: str, data: Optional[Any] = None, timeout: int = 30
     ) -> requests.Response | Tuple[requests.Response, Optional[Dict[str, Any] | str]]:
         """Execute the request."""
+        # Encode string data as UTF-8 to handle Unicode characters
+        if isinstance(data, str):
+            data = data.encode("utf-8")
+
         resp: requests.Response = self.r.request(method, self.endpoint + url, data=data, timeout=timeout)
 
         # Some massaging to make our gerrit python code work
index 28314d1..f71d7c0 100644 (file)
@@ -171,7 +171,7 @@ class Gerrit(client.RestApi):
 
         POST /changes/{change-id}/revisions/{revision-id}/review
         """
-        log.info(fqdn, gerrit_project, changeid)
+        log.info(f"Voting on change: fqdn={fqdn}, project={gerrit_project}, changeid={changeid}")
         access_str = "changes/{}/revisions/2/review".format(changeid)
         headers = {"Content-Type": "application/json; charset=UTF-8"}
         self.r.headers.update(headers)
index 2de9cf2..a2cd92b 100644 (file)
@@ -68,7 +68,7 @@ def submit_pr(ctx, organization, repo, pr):
 def votes(ctx, organization, repo, pr):
     """Helper for votes."""
     approval_list = prvotes(organization, repo, pr)
-    log.info("Approvals:", approval_list)
+    log.info(f"Approvals: {approval_list}")
 
 
 @click.command(name="list")
@@ -109,7 +109,7 @@ def createrepo(ctx, organization, repository, description, has_issues, has_proje
     has_issues = has_issues or False
     has_wiki = has_wiki or False
     has_projects = has_projects or False
-    log.info("Creating repo under organization: ", orgName)
+    log.info(f"Creating repo under organization: {orgName}")
     try:
         org = g.get_organization(orgName)
     except GithubException as ghe:
index 51c8a17..f8d3d29 100644 (file)
@@ -96,7 +96,7 @@ class Nexus:
             }
         }
 
-        json_data = json.dumps(target).encode(encoding="latin-1")
+        json_data = json.dumps(target).encode(encoding="utf-8")
 
         r = requests.post(url, auth=self.auth, headers=self.headers, data=json_data)
 
@@ -148,7 +148,7 @@ class Nexus:
             }
         }
 
-        json_data = json.dumps(privileges).encode(encoding="latin-1")
+        json_data = json.dumps(privileges).encode(encoding="utf-8")
         r = requests.post(url, auth=self.auth, headers=self.headers, data=json_data)
         privileges = r.json()
 
@@ -188,7 +188,7 @@ class Nexus:
             }
         }
 
-        json_data = json.dumps(role).encode(encoding="latin-1")
+        json_data = json.dumps(role).encode(encoding="utf-8")
         log.debug("Sending role {} to Nexus".format(json_data))
 
         r = requests.post(url, auth=self.auth, headers=self.headers, data=json_data)
@@ -243,7 +243,7 @@ class Nexus:
         for role in extra_roles:
             user["data"]["roles"].append(self.get_role(role))
 
-        json_data = json.dumps(user).encode(encoding="latin-1")
+        json_data = json.dumps(user).encode(encoding="utf-8")
 
         user = requests.post(url, auth=self.auth, headers=self.headers, data=json_data)
 
@@ -274,7 +274,7 @@ class Nexus:
 
         repo = {"data": data}
 
-        json_data = json.dumps(repo).encode(encoding="latin-1")
+        json_data = json.dumps(repo).encode(encoding="utf-8")
 
         requests.put(url, auth=self.auth, headers=self.headers, data=json_data)
 
diff --git a/releasenotes/notes/fix-unicode-encoding-logging-c891ffd99f38c7f5.yaml b/releasenotes/notes/fix-unicode-encoding-logging-c891ffd99f38c7f5.yaml
new file mode 100644 (file)
index 0000000..8aa11fb
--- /dev/null
@@ -0,0 +1,15 @@
+---
+fixes:
+  - |
+    Fix UnicodeEncodeError in Gerrit API requests when responses contain
+    Unicode characters. JSON string data is now properly encoded as UTF-8
+    in the RestApi._request() method, preventing 'latin-1' codec errors
+    when user names or group names contain special characters (e.g., 'š').
+
+    This fixes failures in commands like:
+      lftools gerrit addgithubrights <fqdn> <project>
+
+  - |
+    Fix logging TypeError in GitHub create-repo command. Changed from
+    using comma-separated arguments (which caused format string errors)
+    to using f-string formatting for proper message construction.