From 3135c8b7e39e6f0ea87f6eec6ff8ce2bf7164a2b Mon Sep 17 00:00:00 2001 From: Mark Paronyan Date: Wed, 30 Sep 2026 20:58:12 +0300 Subject: [PATCH] refactor: use f-strings in remaining modules, scripts and tests --- gns3server/crash_report.py | 6 +-- gns3server/db/models/base.py | 4 +- gns3server/server.py | 4 +- gns3server/utils/__init__.py | 2 +- gns3server/utils/asyncio/aiozipstream.py | 2 +- gns3server/utils/images.py | 2 +- gns3server/utils/interfaces.py | 4 +- scripts/welcome.py | 42 ++++++++------------ tests/api/routes/controller/test_nodes.py | 12 ++---- tests/api/routes/controller/test_projects.py | 21 +++++----- tests/api/routes/controller/test_version.py | 4 +- tests/test_topologies.py | 30 +++++++------- 12 files changed, 55 insertions(+), 78 deletions(-) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 5d407db4a..8e9e108ad 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -77,14 +77,14 @@ class CrashReport: dsn=CrashReport.DSN, release=__version__, default_integrations=False, integrations=[sentry_logging] ) except Exception as e: - log.error("Crash report could not be sent: {}".format(e)) + log.error(f"Crash report could not be sent: {e}") return tags = { "os:name": platform.system(), "os:release": platform.release(), "os:win_32": " ".join(platform.win32_ver()), - "os:mac": "{} {}".format(platform.mac_ver()[0], platform.mac_ver()[2]), + "os:mac": f"{platform.mac_ver()[0]} {platform.mac_ver()[2]}", "os:linux": distro.name(pretty=True), } @@ -93,7 +93,7 @@ class CrashReport: scope.set_tag(key, value) extra_context = { - "python:version": "{}.{}.{}".format(sys.version_info[0], sys.version_info[1], sys.version_info[2]), + "python:version": f"{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}", "python:bit": struct.calcsize("P") * 8, "python:encoding": sys.getdefaultencoding(), "python:frozen": "{}".format(hasattr(sys, "frozen")), diff --git a/gns3server/db/models/base.py b/gns3server/db/models/base.py index 522486e93..45190577a 100644 --- a/gns3server/db/models/base.py +++ b/gns3server/db/models/base.py @@ -59,10 +59,10 @@ class GUID(TypeDecorator): return str(value) else: if not isinstance(value, uuid.UUID): - return "{:032x}".format(uuid.UUID(value).int) + return f"{uuid.UUID(value).int:032x}" else: # hexstring - return "{:032x}".format(value.int) + return f"{value.int:032x}" def process_result_value(self, value, dialect): if value is None: diff --git a/gns3server/server.py b/gns3server/server.py index f9824caab..3ec16e456 100644 --- a/gns3server/server.py +++ b/gns3server/server.py @@ -260,9 +260,7 @@ class Server: raise SystemExit("Python 3.9 or higher is required") log.info( - "Running with Python {major}.{minor}.{micro} and has PID {pid}".format( - major=sys.version_info[0], minor=sys.version_info[1], micro=sys.version_info[2], pid=os.getpid() - ) + f"Running with Python {sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]} and has PID {os.getpid()}" ) # check for the correct locale (UNIX/Linux only) diff --git a/gns3server/utils/__init__.py b/gns3server/utils/__init__.py index 286a76757..b1ba96c7d 100644 --- a/gns3server/utils/__init__.py +++ b/gns3server/utils/__init__.py @@ -48,7 +48,7 @@ def int_to_macaddress(integer): """ Convert an integer to a mac address """ - return ":".join(textwrap.wrap("{:012x}".format(integer), width=2)) + return ":".join(textwrap.wrap(f"{integer:012x}", width=2)) def parse_version(version): diff --git a/gns3server/utils/asyncio/aiozipstream.py b/gns3server/utils/asyncio/aiozipstream.py index 07770ed0e..fc6f5fdc3 100644 --- a/gns3server/utils/asyncio/aiozipstream.py +++ b/gns3server/utils/asyncio/aiozipstream.py @@ -184,7 +184,7 @@ class ZipFile(zipfile.ZipFile): """ if not isinstance(comment, bytes): - raise TypeError("comment: expected bytes, got {}".format(type(comment))) + raise TypeError(f"comment: expected bytes, got {type(comment)}") # check for valid comment length if len(comment) >= zipfile.ZIP_MAX_COMMENT: if self.debug: diff --git a/gns3server/utils/images.py b/gns3server/utils/images.py index 4f6e402bb..4db2c2f8d 100644 --- a/gns3server/utils/images.py +++ b/gns3server/utils/images.py @@ -70,7 +70,7 @@ async def list_images(image_type): for root, _, filenames in _os_walk(directory, recurse=recurse): for filename in filenames: if filename in files: - log.debug("File {} has already been found, skipping...".format(filename)) + log.debug(f"File {filename} has already been found, skipping...") continue if filename.endswith(".md5sum") or filename.startswith("."): continue diff --git a/gns3server/utils/interfaces.py b/gns3server/utils/interfaces.py index 0585028c9..34f9422b6 100644 --- a/gns3server/utils/interfaces.py +++ b/gns3server/utils/interfaces.py @@ -66,7 +66,7 @@ def _get_windows_interfaces_from_registry(): if ip_address: # get the first IPv4 address only ip_address = ip_address[0] - npf_interface = "\\Device\\NPF_{guid}".format(guid=guid) + npf_interface = f"\\Device\\NPF_{guid}" interfaces.append( { "id": npf_interface, @@ -116,7 +116,7 @@ def get_windows_interfaces(): ip_address = network_config.IPAddress[0] netmask = network_config.IPSubnet[0] break - npf_interface = "\\Device\\NPF_{guid}".format(guid=adapter.GUID) + npf_interface = f"\\Device\\NPF_{adapter.GUID}" interfaces.append( { "id": npf_interface, diff --git a/scripts/welcome.py b/scripts/welcome.py index d9ecd21a0..7b1ed7d35 100644 --- a/scripts/welcome.py +++ b/scripts/welcome.py @@ -39,7 +39,7 @@ class Welcome_dialog: if self.gns3_version() is None: self.display.set_background_title("GNS3") else: - self.display.set_background_title("GNS3 {}".format(self.gns3_version())) + self.display.set_background_title(f"GNS3 {self.gns3_version()}") def get_ip(self): """ @@ -228,42 +228,36 @@ class Welcome_dialog: if answer != self.display.OK: return if destination == self.get_ip(): - self.display.msgbox("The destination cannot be the same as this VM IP address ({})".format(destination)) + self.display.msgbox(f"The destination cannot be the same as this VM IP address ({destination})") return if option == "Send": # first make sure they are no files belonging to root os.system("sudo chown -R gns3:gns3 /opt/gns3") # then rsync the data - command = r"rsync -az --progress -e 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /home/gns3/.ssh/gns3-vm-key' /opt/gns3 gns3@{}:/opt".format( - destination - ) - ret = os.system('bash -c "{}"'.format(command)) + command = rf"rsync -az --progress -e 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /home/gns3/.ssh/gns3-vm-key' /opt/gns3 gns3@{destination}:/opt" + ret = os.system(f'bash -c "{command}"') time.sleep(10) if ret != 0: - self.display.msgbox("Could not send data to the other GNS3 VM located at {}".format(destination)) + self.display.msgbox(f"Could not send data to the other GNS3 VM located at {destination}") else: self.display.msgbox( - "Images and projects have been successfully sent to the other GNS3 VM located at {}".format( - destination - ) + f"Images and projects have been successfully sent to the other GNS3 VM located at {destination}" ) elif option == "Setup": - script = """ + script = f""" if [ ! -f ~/.ssh/gns3-vm-key ] then - ssh-keygen -f ~/.ssh/gns3-vm-key -N '' -C gns3@{} + ssh-keygen -f ~/.ssh/gns3-vm-key -N '' -C gns3@{self.get_ip()} fi - ssh-copy-id -i ~/.ssh/gns3-vm-key gns3@{} - """.format(self.get_ip(), destination) - ret = os.system('bash -c "{}"'.format(script)) + ssh-copy-id -i ~/.ssh/gns3-vm-key gns3@{destination} + """ + ret = os.system(f'bash -c "{script}"') time.sleep(10) if ret != 0: self.display.msgbox("Error while setting up the migrate feature") else: self.display.msgbox( - "Configuration successful, you can now send data to the GNS3 VM located at {} without password".format( - destination - ) + f"Configuration successful, you can now send data to the GNS3 VM located at {destination} without password" ) def shrink_disk(self): @@ -300,11 +294,7 @@ class Welcome_dialog: if version is None: content += "GNS3 is not installed please install it with sudo pip3 install gns3-server. Or download a preinstalled VM.\n\n" else: - content = ( - "GNS3 version: {gns3_version}\nVM version: {gns3vm_version}\nKVM support available: {kvm}\n\n".format( - gns3vm_version=self.gns3vm_version(), gns3_version=version, kvm=self.kvm_support() - ) - ) + content = f"GNS3 version: {version}\nVM version: {self.gns3vm_version()}\nKVM support available: {self.kvm_support()}\n\n" ip = self.get_ip() @@ -336,7 +326,7 @@ Images and projects are located in /opt/gns3 try: response = urllib.request.urlopen("http://pypi.python.org/", timeout=5) except urllib.request.URLError as err: - self.display.infobox("Can't connect to Internet (pypi.python.org): {}".format(str(err))) + self.display.infobox(f"Can't connect to Internet (pypi.python.org): {err!s}") time.sleep(15) return self.display.infobox("Connection to Internet: OK") @@ -384,7 +374,7 @@ Images and projects are located in /opt/gns3 major_version = self.gns3_major_version() if major_version == "2.2": - os.system("nano ~/.config/GNS3/{}/gns3_server.conf".format(major_version)) + os.system(f"nano ~/.config/GNS3/{major_version}/gns3_server.conf") else: os.system("nano ~/.config/GNS3/gns3_server.conf") @@ -474,7 +464,7 @@ Images and projects are located in /opt/gns3 try: while True: code, tag = self.display.menu( - "GNS3 {}".format(self.gns3_version()), + f"GNS3 {self.gns3_version()}", choices=[ ("Information", "Display VM information"), ("Upgrade", "Upgrade GNS3"), diff --git a/tests/api/routes/controller/test_nodes.py b/tests/api/routes/controller/test_nodes.py index a2d37922a..cf441a01f 100644 --- a/tests/api/routes/controller/test_nodes.py +++ b/tests/api/routes/controller/test_nodes.py @@ -465,9 +465,7 @@ class TestNodeRoutes: compute.http_query.assert_called_with( "GET", - "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format( - project_id=project.id, node_id=node.id - ), + f"/projects/{project.id}/files/project-files/vpcs/{node.id}/hello", timeout=None, stream=True, ) @@ -491,16 +489,12 @@ class TestNodeRoutes: compute.http_query.assert_called_once() call_args = compute.http_query.call_args assert call_args[0][0] == "POST" - assert call_args[0][1] == "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format( - project_id=project.id, node_id=node.id - ) + assert call_args[0][1] == f"/projects/{project.id}/files/project-files/vpcs/{node.id}/hello" assert call_args[1]["timeout"] is None # data should be an async generator from request.stream() assert hasattr(call_args[1]["data"], "__aiter__") - response = await client.get( - "/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id) - ) + response = await client.get(f"/projects/{project.id}/nodes/{node.id}/files/../hello") assert response.status_code == status.HTTP_404_NOT_FOUND # @pytest.mark.asyncio diff --git a/tests/api/routes/controller/test_projects.py b/tests/api/routes/controller/test_projects.py index 3ae35c689..d93905ca0 100644 --- a/tests/api/routes/controller/test_projects.py +++ b/tests/api/routes/controller/test_projects.py @@ -244,10 +244,9 @@ class TestControllerProjectRoutes: ) assert response.status_code == status.HTTP_200_OK assert response.headers["CONTENT-TYPE"] == "application/gns3project" - assert response.headers[ - "CONTENT-DISPOSITION" - ] == "attachment; filename=\"{name}.gns3project\"; filename*=UTF-8''{name}.gns3project".format( - name=project.name + assert ( + response.headers["CONTENT-DISPOSITION"] + == f"attachment; filename=\"{project.name}.gns3project\"; filename*=UTF-8''{project.name}.gns3project" ) with open(str(tmpdir / "project.zip"), "wb+") as f: @@ -283,10 +282,9 @@ class TestControllerProjectRoutes: ) assert response.status_code == status.HTTP_200_OK assert response.headers["CONTENT-TYPE"] == "application/gns3project" - assert response.headers[ - "CONTENT-DISPOSITION" - ] == "attachment; filename=\"{name}.gns3project\"; filename*=UTF-8''{name}.gns3project".format( - name=project.name + assert ( + response.headers["CONTENT-DISPOSITION"] + == f"attachment; filename=\"{project.name}.gns3project\"; filename*=UTF-8''{project.name}.gns3project" ) with open(str(tmpdir / "project.zip"), "wb+") as f: f.write(response.content) @@ -343,10 +341,9 @@ class TestControllerProjectRoutes: if response.status_code == status.HTTP_200_OK: assert response.headers["CONTENT-TYPE"] == "application/gns3project" - assert response.headers[ - "CONTENT-DISPOSITION" - ] == "attachment; filename=\"{name}.gns3project\"; filename*=UTF-8''{name}.gns3project".format( - name=project.name + assert ( + response.headers["CONTENT-DISPOSITION"] + == f"attachment; filename=\"{project.name}.gns3project\"; filename*=UTF-8''{project.name}.gns3project" ) with open(str(tmpdir / "project.zip"), "wb+") as f: f.write(response.content) diff --git a/tests/api/routes/controller/test_version.py b/tests/api/routes/controller/test_version.py index b0ea854aa..39fd1ee17 100644 --- a/tests/api/routes/controller/test_version.py +++ b/tests/api/routes/controller/test_version.py @@ -42,9 +42,7 @@ class TestVersionRoutes: params = {"version": "0.4.2"} response = await client.post(app.url_path_for("check_version"), json=params) assert response.status_code == status.HTTP_409_CONFLICT - assert response.json() == { - "message": "Client version 0.4.2 is not the same as server version {}".format(__version__) - } + assert response.json() == {"message": f"Client version 0.4.2 is not the same as server version {__version__}"} async def test_version_invalid_input_schema(self, app: FastAPI, client: AsyncClient) -> None: diff --git a/tests/test_topologies.py b/tests/test_topologies.py index c4320658f..0bbc637a4 100644 --- a/tests/test_topologies.py +++ b/tests/test_topologies.py @@ -46,15 +46,15 @@ def test_convert(directory, tmpdir): before_directory = os.path.join(topologies_directory, directory, "before") after_directory = os.path.join(topologies_directory, directory, "after") - assert os.path.exists(before_directory), "No before directory found file for {}".format(directory) - assert os.path.exists(after_directory), "No after directory found file for {}".format(directory) + assert os.path.exists(before_directory), f"No before directory found file for {directory}" + assert os.path.exists(after_directory), f"No after directory found file for {directory}" gns3_file = None for file in os.listdir(before_directory): if file.endswith(".gns3"): gns3_file = file - assert gns3_file, "No .gns3 found file for {}".format(before_directory) + assert gns3_file, f"No .gns3 found file for {before_directory}" with open(os.path.join(before_directory, gns3_file)) as f: before_topology = json.load(f) @@ -76,19 +76,19 @@ def test_convert(directory, tmpdir): for file in files: directory = os.path.relpath(root, after_directory) file_path = os.path.join(work_directory, directory, file) - assert os.path.exists(file_path), "{} is missing".format(os.path.join(directory, file)) + assert os.path.exists(file_path), f"{os.path.join(directory, file)} is missing" # For gns3project we check if size are not too much differents if file_path.endswith(".gns3project"): size = os.stat(file_path).st_size other_size = os.stat(os.path.join(os.path.join(root, file))).st_size - assert size in range(other_size - 100, other_size + 100), "File {} is different".format( - os.path.join(directory, file) + assert size in range(other_size - 100, other_size + 100), ( + f"File {os.path.join(directory, file)} is different" ) # For non .gns3 file we check if the file are the same elif not file_path.endswith(".gns3"): assert os.stat(file_path).st_size == os.stat(os.path.join(os.path.join(root, file))).st_size, ( - "File {} is different".format(os.path.join(directory, file)) + f"File {os.path.join(directory, file)} is different" ) # Check if we don't have unexpected file in work directory @@ -98,7 +98,7 @@ def test_convert(directory, tmpdir): file_path = os.path.join(after_directory, directory, file) # .backup are created by the conversion process if ".backup" not in file_path: - assert os.path.exists(file_path), "{} should not be here".format(os.path.join(directory, file)) + assert os.path.exists(file_path), f"{os.path.join(directory, file)} should not be here" with open(os.path.join(after_directory, gns3_file)) as f: after_topology = json.load(f) @@ -109,12 +109,12 @@ def compare_dict(path, source, reference): """ Compare two dictionary of a topology """ - assert isinstance(source, dict), "Source is not a dict in {}".format(path) + assert isinstance(source, dict), f"Source is not a dict in {path}" for key in source: - assert key in reference, "Unexpected {} in {} it should be {}".format(key, source, reference) + assert key in reference, f"Unexpected {key} in {source} it should be {reference}" for key in sorted(reference.keys()): val = reference[key] - assert key in source, "{} is missing in {}".format(key, source) + assert key in source, f"{key} is missing in {source}" if ( isinstance(val, str) or isinstance(val, float) @@ -133,12 +133,12 @@ def compare_dict(path, source, reference): elif key == "version": assert source[key] == __version__ else: - assert val == source[key], "Wrong value for {}: \n{}\nit should be\n{}".format(key, source[key], val) + assert val == source[key], f"Wrong value for {key}: \n{source[key]}\nit should be\n{val}" elif isinstance(val, dict): compare_dict(path + key + "/", source[key], val) elif isinstance(val, list): - assert len(val) == len(source[key]), "Not enough value in {} ({}/{}) it shoud be {} not {}".format( - key, len(val), len(source[key]), val, source[key] + assert len(val) == len(source[key]), ( + f"Not enough value in {key} ({len(val)}/{len(source[key])}) it shoud be {val} not {source[key]}" ) for idx, element in enumerate(source[key]): if isinstance(element, dict): @@ -146,4 +146,4 @@ def compare_dict(path, source, reference): else: assert element == val[idx] else: - assert False, "Value type for {} is not supported".format(key) + assert False, f"Value type for {key} is not supported"