From db10aadb6fa3885461509eccfe484d4d86963a83 Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Tue, 2 Jun 2026 14:22:45 +0200 Subject: [PATCH] Removed requests dependency Ticket: CFE-4188 Signed-off-by: Victor Moene --- requirements.txt | 1 - tests/test_bot.py | 4 +-- tom/changelog.py | 3 +- tom/dependencies.py | 10 +++--- tom/github.py | 11 +++--- tom/jenkins.py | 10 +++--- tom/main.py | 2 -- tom/network.py | 82 +++++++++++++++++++++++++++++++++++++++++++++ tom/packages.py | 6 ++-- tom/slack.py | 4 +-- tom/tag.py | 1 - 11 files changed, 105 insertions(+), 29 deletions(-) create mode 100644 tom/network.py diff --git a/requirements.txt b/requirements.txt index bd6f234..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +0,0 @@ -requests==2.32.4 diff --git a/tests/test_bot.py b/tests/test_bot.py index dc14669..d5a398e 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -248,8 +248,8 @@ def test_slow_docs_build(): print("bot = {}".format(bot)) -@patch("tom.jenkins.requests") -@patch("tom.github.requests") +@patch("tom.jenkins.network") +@patch("tom.github.network") def _trigger_build( github_requests, jenkins_requests, prs, comment, repo, base_branch="master" ): diff --git a/tom/changelog.py b/tom/changelog.py index 1a23bd9..5fbd0de 100644 --- a/tom/changelog.py +++ b/tom/changelog.py @@ -1,6 +1,5 @@ import re import os -import requests import subprocess import datetime import hashlib @@ -125,7 +124,7 @@ def generate_changelog_in_repo(self, branch, repo): return False # find old and new versions - (header, old_version, old_changelog) = self.split_changelog_into_parts( + header, old_version, old_changelog = self.split_changelog_into_parts( changelog_filename, repo ) if header != "": diff --git a/tom/dependencies.py b/tom/dependencies.py index 08976f0..1d0f6df 100644 --- a/tom/dependencies.py +++ b/tom/dependencies.py @@ -1,6 +1,5 @@ import re import json -import requests import collections import datetime import hashlib @@ -8,6 +7,7 @@ import logging as log from tom.git import GitRepo from tom.utils import pretty +import tom.network as network class DependencyException(Exception): @@ -137,7 +137,7 @@ def checkfile(self, url, sha256=False): try: if not sha256 and url.startswith("http"): log.debug("testing with HEAD") - r = requests.head(url) + r = network.head(url) return r.status_code >= 200 and r.status_code < 300 else: log.debug("getting whole file") @@ -222,7 +222,7 @@ def get_version_from_monitoring(self, dep): id = self.monitoring_ids[dep] url = "https://release-monitoring.org/api/v2/versions/?project_id={}".format(id) try: - data = requests.get(url).json() + data = network.get(url).json() except: raise ReleaseMonitoringException( "Failed to do a request to release-monitoring.org website" @@ -255,7 +255,7 @@ def get_current_version(self, dep): dist_file = self.buildscripts.get_file(dist_file_path) dist_file = dist_file.strip() old_filename = re.sub(".* ", "", dist_file) - (old_version, separator) = self.extract_version_from_filename(dep, old_filename) + old_version, separator = self.extract_version_from_filename(dep, old_filename) return old_version def patch_spec_file(self, spec_file_path, old_version, new_version): @@ -283,7 +283,7 @@ def update_single_dep(self, dep): source_file = source_file.strip() old_filename = re.sub(".* ", "", dist_file) old_url = "{}{}".format(source_file, old_filename) - (old_version, separator) = self.extract_version_from_filename(dep, old_filename) + old_version, separator = self.extract_version_from_filename(dep, old_filename) new_version = self.get_version_from_monitoring(dep) if not new_version: log.warning( diff --git a/tom/github.py b/tom/github.py index b73f3c1..c994f2b 100644 --- a/tom/github.py +++ b/tom/github.py @@ -1,14 +1,13 @@ import random -import requests import re import os import sys import json import datetime import logging as log -import requests from copy import copy +import tom.network as network from tom.utils import pretty, write_json # Global constant, reused many times for finding emails in comments: @@ -36,7 +35,7 @@ def get(self, path): if path in self.get_cache: log.debug("Found in cache") return self.get_cache[path] - r = requests.get(path, headers=self.headers) + r = network.get(path, headers=self.headers) log.debug("RESPONSE {}".format(r.status_code)) if not (200 <= r.status_code < 300): @@ -74,7 +73,7 @@ def post(self, path, data, check_status_code=True): return None self.api_log("POST {} {}".format(path, data)) path = self.path(path) - r = requests.post(path, headers=self.headers, json=data) + r = network.post(path, headers=self.headers, json=data) log.debug("RESPONSE {}".format(r.status_code)) if check_status_code: assert r.status_code >= 200 and r.status_code < 300, r.text @@ -388,7 +387,7 @@ def create_prs_from_slack(self): last_branch = self.find_last_branch_in_repo(username, repo) log.info("last branch: " + last_branch) # now, try to find a parent for it. - (parent_repo, parent_branch) = self.find_parent(username, repo, last_branch) + parent_repo, parent_branch = self.find_parent(username, repo, last_branch) log.info("parent branch: " + parent_branch) pr_text = self.github.create_pr( parent_repo, parent_branch, username, last_branch, last_branch + " PR", "" @@ -507,7 +506,7 @@ def __init__(self, data, github): log.info("Found related PR in {}: #{}".format(repo, repo_pr)) self.merge_with[repo] = repo_pr - # All of these require extra API requests, and will be completed + # All of these require extra API network, and will be completed # by the @property getter functions below only when necessary. # (The bot can be configured with more or less features in config.json) diff --git a/tom/jenkins.py b/tom/jenkins.py index 6a93325..64a9892 100644 --- a/tom/jenkins.py +++ b/tom/jenkins.py @@ -1,7 +1,7 @@ import logging as log from time import sleep -import requests -from requests.auth import HTTPBasicAuth +import base64 +import tom.network as network from tom.utils import pretty import os from typing import Dict @@ -20,7 +20,7 @@ def __init__(self, url, job, secrets, username): self.crumb = crumb self.username = username - self.auth = HTTPBasicAuth(user, token) + self.auth = base64.b64encode(("%s:%s" % (user, token)).encode()).decode() self.headers = {} if crumb: @@ -34,7 +34,7 @@ def post(self, path, data): if os.getenv("TOM") == "PASSIVE": print("Would post: " + path) return None - r = requests.post(path, data=data, headers=self.headers, auth=self.auth) + r = network.post(path, data=data, headers=self.headers, auth=self.auth) if not (200 <= r.status_code < 300): log.error("Unexpected HTTP response from Jenkins: {}".format(r.status_code)) log.error(str(r.headers)) @@ -144,7 +144,7 @@ def wait_for_queue(self, url): while "executable" not in queue_item: log.info("Waiting for jenkins build in queue") sleep(1) - r = requests.get(url + "api/json", headers=self.headers, auth=self.auth) + r = network.get(url + "api/json", headers=self.headers, auth=self.auth) assert r.status_code >= 200 and r.status_code < 300 queue_item = r.json() log.debug(pretty(queue_item)) diff --git a/tom/main.py b/tom/main.py index 76ce85b..1e053ae 100644 --- a/tom/main.py +++ b/tom/main.py @@ -113,8 +113,6 @@ def main(): log.basicConfig(level=numeric_level, format=fmt) else: log.basicConfig(format=fmt) - log.getLogger("requests").setLevel(log.WARNING) - log.getLogger("urllib3").setLevel(log.WARNING) if args.talk_user: run_talk(args.directory, args.talk_user, args.interactive) else: diff --git a/tom/network.py b/tom/network.py new file mode 100644 index 0000000..f71909a --- /dev/null +++ b/tom/network.py @@ -0,0 +1,82 @@ +from urllib.request import Request, urlopen +from urllib.error import HTTPError +from urllib.parse import urlencode +from json import dumps, loads + + +class Response: + + def __init__(self): + self.status_code = 0 + self.headers = {} + self.text = "" + + def json(self): + return loads(self.text) + + +def _handle_request( + url, data=None, headers={}, base64auth=None, method=None, json=None +): + """ + Handle a request with urllib + + Args: + url: The url of the request + data: The data to send in the body of the request. + headers: A dictionary of HTTP headers to send with the request. + base64auth: A Base64-encoded string for HTTP Basic Authentication. + method: The HTTP method to use (e.g., 'GET', 'POST', 'PUT') + json: A JSON-serializable Python object to send in the body of the request. Overrides `data` + """ + + encoded_data = dumps(data).encode("utf-8") if data is not None else None + + if json is not None: + encoded_data = dumps(json).encode("utf-8") + if "Content-Type" not in headers: + headers["Content-Type"] = "application/json" + + elif data is not None: + if isinstance(data, dict): + encoded_data = urlencode(data).encode("utf-8") + if "Content-Type" not in headers: + headers["Content-Type"] = "application/x-www-form-urlencoded" + elif isinstance(data, bytes): + encoded_data = data + else: + encoded_data = str(data).encode("utf-8") + + req = Request(url, data=encoded_data, headers=headers, method=method) + if base64auth: + req.add_header("Authorization", "Basic %s" % base64auth) + + result = Response() + + try: + with urlopen(req) as r: + result.text = r.read().decode("utf-8") + result.status_code = r.code + result.headers = r.headers + except HTTPError as e: + result.status_code = e.code + + return result + + +def post(url, data=None, json=None, headers={}, auth=None): + return _handle_request( + url, data=data, headers=headers, base64auth=auth, method="POST", json=json + ) + + +def get(url, headers={}, auth=None): + return _handle_request( + url, data=None, headers=headers, base64auth=auth, method="GET" + ) + + +def head(url, headers={}, auth=None): + return _handle_request( + url, data=None, headers=headers, base64auth=auth, method="HEAD" + ) diff --git a/tom/packages.py b/tom/packages.py index e431109..1d9b181 100644 --- a/tom/packages.py +++ b/tom/packages.py @@ -1,8 +1,8 @@ import re import json -import requests import collections import datetime +import tom.network as network from tom.git import GitRepo @@ -125,7 +125,7 @@ def run(self, inputs): releases_url = ( "https://cfengine.com/release-data/%s/releases.json" % product ) - releases_request = requests.get(releases_url) + releases_request = network.get(releases_url) if not releases_request.ok: raise URLDownloadFailureException( "failed to download %s, return code %d" @@ -233,7 +233,7 @@ def run(self, inputs): def collect_packages(self, url): """Given a release URL, returns a dict where keys are platform names, and values are {'url': 'http...'}""" - release_request = requests.get(url) + release_request = network.get(url) if not release_request.ok: raise URLDownloadFailureException( "failed to download %s, return code %d" diff --git a/tom/slack.py b/tom/slack.py index a0e0d74..a5f6ed3 100644 --- a/tom/slack.py +++ b/tom/slack.py @@ -3,9 +3,9 @@ import re import sys import json -import requests import logging as log from tom.utils import pretty +import tom.network as network class Slack: @@ -36,7 +36,7 @@ def post(self, url, data={}): url = self.api(url) if not "token" in data: data["token"] = self.bot_token - r = requests.post(url, data) + r = network.post(url, data) assert r.status_code >= 200 and r.status_code < 300 try: log.debug(pretty(r.json())) diff --git a/tom/tag.py b/tom/tag.py index cb83a36..62e1c9b 100644 --- a/tom/tag.py +++ b/tom/tag.py @@ -1,6 +1,5 @@ import re import os -import requests import subprocess import datetime import hashlib