From e574db8b4e72793add243a1ba237ba8bb3e09179 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Mon, 20 Apr 2026 21:35:16 +0000 Subject: [PATCH 01/14] fix: clarify contribution guidelines --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aa6bff3ad1da..176dfaa76723 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Before contributing -Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before submitting your pull requests, please ensure that you __read the whole guidelines__. If you have any doubts about the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community on [Gitter](https://gitter.im/TheAlgorithms/community). +Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Before submitting your pull requests, please ensure that you __read the whole guidelines carefully__. If you have any doubts about the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Python/issues/new) or ask the community on [Gitter](https://gitter.im/TheAlgorithms/community). ## Contributing @@ -21,7 +21,7 @@ __Improving comments__ and __writing proper tests__ are also highly welcome. ### Contribution -We appreciate any contribution, from fixing a grammar mistake in a comment to implementing complex algorithms. Please read this section if you are contributing your work. +We appreciate any contribution, from fixing a grammatical mistake in a comment to implementing complex algorithms. Please read this section if you are contributing your work. Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help. From f312a816c6135e7961b541741a7443d2c9091896 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Wed, 6 May 2026 15:34:17 +0000 Subject: [PATCH 02/14] fix dfs_recursive to iterate neighbors instead of all vertices --- graphs/depth_first_search_2.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/graphs/depth_first_search_2.py b/graphs/depth_first_search_2.py index 8fe48b7f2b42..e352ffdcb90a 100644 --- a/graphs/depth_first_search_2.py +++ b/graphs/depth_first_search_2.py @@ -71,14 +71,12 @@ def dfs(self) -> None: 0 1 2 3 """ # visited array for storing already visited nodes - visited = [False] * len(self.vertex) + visited = {v: False for v in self.vertex} + for vertex in self.vertex: + if not visited[vertex]: + self.dfs_recursive(vertex, visited) - # call the recursive helper function - for i in range(len(self.vertex)): - if not visited[i]: - self.dfs_recursive(i, visited) - - def dfs_recursive(self, start_vertex: int, visited: list) -> None: + def dfs_recursive(self, start_vertex: int, visited: dict) -> None: """ Perform a recursive depth-first search (DFS) traversal on the graph. @@ -93,7 +91,7 @@ def dfs_recursive(self, start_vertex: int, visited: list) -> None: >>> g.add_edge(2, 0) >>> g.add_edge(2, 3) >>> g.add_edge(3, 3) - >>> visited = [False] * len(g.vertex) + >>> visited = {v: False for v in g.vertex} >>> g.dfs_recursive(0, visited) 0 1 2 3 """ @@ -103,10 +101,10 @@ def dfs_recursive(self, start_vertex: int, visited: list) -> None: print(start_vertex, end="") # Recur for all the vertices that are adjacent to this node - for i in self.vertex: - if not visited[i]: + for neighbor in self.vertex[start_vertex]: + if not visited[neighbor]: print(" ", end="") - self.dfs_recursive(i, visited) + self.dfs_recursive(neighbor, visited) if __name__ == "__main__": From 505cbc79eca817fb14dd7b22ef706bc5bf68f6fb Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Wed, 6 May 2026 15:36:16 +0000 Subject: [PATCH 03/14] close file handle with 'with' statement in problem_099 solution and fix Python 3 multi-exception syntax in instagram_crawler --- project_euler/problem_099/sol1.py | 11 ++++++----- web_programming/instagram_crawler.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/project_euler/problem_099/sol1.py b/project_euler/problem_099/sol1.py index bf5621c6583c..edde788562b2 100644 --- a/project_euler/problem_099/sol1.py +++ b/project_euler/problem_099/sol1.py @@ -24,11 +24,12 @@ def solution(data_file: str = "base_exp.txt") -> int: """ largest: float = 0 result = 0 - for i, line in enumerate(open(os.path.join(os.path.dirname(__file__), data_file))): - a, x = list(map(int, line.split(","))) - if x * log10(a) > largest: - largest = x * log10(a) - result = i + 1 + with open(os.path.join(os.path.dirname(__file__), data_file)) as file: + for i, line in enumerate(file): + a, x = list(map(int, line.split(","))) + if x * log10(a) > largest: + largest = x * log10(a) + result = i + 1 return result diff --git a/web_programming/instagram_crawler.py b/web_programming/instagram_crawler.py index 0b91db01ca09..68271c1c4643 100644 --- a/web_programming/instagram_crawler.py +++ b/web_programming/instagram_crawler.py @@ -53,7 +53,7 @@ def get_json(self) -> dict: scripts = BeautifulSoup(html, "html.parser").find_all("script") try: return extract_user_profile(scripts[4]) - except json.decoder.JSONDecodeError, KeyError: + except (json.decoder.JSONDecodeError, KeyError): return extract_user_profile(scripts[3]) def __repr__(self) -> str: From 2b0ee8de98f711a0f0d2156b0695b930d967cf26 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Sat, 16 May 2026 17:59:55 +0000 Subject: [PATCH 04/14] =?UTF-8?q?enigma=5Fmachine:=20token=20input=20loop?= =?UTF-8?q?=20catches=20ValueError=20instead=20of=20bare=20Exception=20?= =?UTF-8?q?=E2=80=94=20int()=20raises=20ValueError=20on=20non-numeric=20in?= =?UTF-8?q?put,=20not=20a=20generic=20Exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hashes/enigma_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashes/enigma_machine.py b/hashes/enigma_machine.py index 0da8e4113de9..d51b0f443abc 100644 --- a/hashes/enigma_machine.py +++ b/hashes/enigma_machine.py @@ -46,7 +46,7 @@ def engine(input_character): try: token = int(input("Please set token:(must be only digits)\n")) break - except Exception as error: + except ValueError as error: print(error) for _ in range(token): rotator() From 4b00d82cdb65b3b0af4f7c20745ac4e73867e1ca Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Sat, 16 May 2026 18:14:20 +0000 Subject: [PATCH 05/14] =?UTF-8?q?catalan=5Fnumbers:=20fix=20Python=202=20e?= =?UTF-8?q?xception=20syntax=20=E2=80=94=20the=20comma=20form=20was=20remo?= =?UTF-8?q?ved=20in=20Python=203,=20use=20parentheses=20instead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dynamic_programming/catalan_numbers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamic_programming/catalan_numbers.py b/dynamic_programming/catalan_numbers.py index a62abe36d670..7b74f2763d43 100644 --- a/dynamic_programming/catalan_numbers.py +++ b/dynamic_programming/catalan_numbers.py @@ -71,7 +71,7 @@ def catalan_numbers(upper_limit: int) -> "list[int]": print(f"The Catalan numbers from 0 through {N} are:") print(catalan_numbers(N)) print("Try another upper limit for the sequence: ", end="") - except NameError, ValueError: + except (NameError, ValueError): print("\n********* Invalid input, goodbye! ************\n") import doctest From 70e69b49430e5fe3fbdd8b44c72c338b5236bfd6 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Fri, 22 May 2026 11:36:48 +0000 Subject: [PATCH 06/14] use Python 3 exception syntax when parsing point coordinates --- divide_and_conquer/convex_hull.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/divide_and_conquer/convex_hull.py b/divide_and_conquer/convex_hull.py index b1ab33cc9415..93f6daf1f88c 100644 --- a/divide_and_conquer/convex_hull.py +++ b/divide_and_conquer/convex_hull.py @@ -124,7 +124,7 @@ def _construct_points( else: try: points.append(Point(p[0], p[1])) - except IndexError, TypeError: + except (IndexError, TypeError): print( f"Ignoring deformed point {p}. All points" " must have at least 2 coordinates." From 7ebb7368419f393b76a40748282f656b8356b782 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Fri, 22 May 2026 11:37:28 +0000 Subject: [PATCH 07/14] use Python 3 exception syntax in solution() --- project_euler/problem_002/sol4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_002/sol4.py b/project_euler/problem_002/sol4.py index 3341aa1d4569..a13d34fd760e 100644 --- a/project_euler/problem_002/sol4.py +++ b/project_euler/problem_002/sol4.py @@ -56,7 +56,7 @@ def solution(n: int = 4000000) -> int: try: n = int(n) - except TypeError, ValueError: + except (TypeError, ValueError): raise TypeError("Parameter n must be int or castable to int.") if n <= 0: raise ValueError("Parameter n must be greater than or equal to one.") From 7b684a28ed3f0982954769c36c8a8dcbb8f27b2f Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Fri, 22 May 2026 11:37:51 +0000 Subject: [PATCH 08/14] use Python 3 exception syntax across problem_003 solutions --- project_euler/problem_003/sol1.py | 2 +- project_euler/problem_003/sol2.py | 2 +- project_euler/problem_003/sol3.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/project_euler/problem_003/sol1.py b/project_euler/problem_003/sol1.py index dbf9a84f68bb..d1c0e61cf1a6 100644 --- a/project_euler/problem_003/sol1.py +++ b/project_euler/problem_003/sol1.py @@ -80,7 +80,7 @@ def solution(n: int = 600851475143) -> int: try: n = int(n) - except TypeError, ValueError: + except (TypeError, ValueError): raise TypeError("Parameter n must be int or castable to int.") if n <= 0: raise ValueError("Parameter n must be greater than or equal to one.") diff --git a/project_euler/problem_003/sol2.py b/project_euler/problem_003/sol2.py index 4c4f88220514..0af0daceed06 100644 --- a/project_euler/problem_003/sol2.py +++ b/project_euler/problem_003/sol2.py @@ -44,7 +44,7 @@ def solution(n: int = 600851475143) -> int: try: n = int(n) - except TypeError, ValueError: + except (TypeError, ValueError): raise TypeError("Parameter n must be int or castable to int.") if n <= 0: raise ValueError("Parameter n must be greater than or equal to one.") diff --git a/project_euler/problem_003/sol3.py b/project_euler/problem_003/sol3.py index 1a454b618f75..e13a0eb74ec1 100644 --- a/project_euler/problem_003/sol3.py +++ b/project_euler/problem_003/sol3.py @@ -44,7 +44,7 @@ def solution(n: int = 600851475143) -> int: try: n = int(n) - except TypeError, ValueError: + except (TypeError, ValueError): raise TypeError("Parameter n must be int or castable to int.") if n <= 0: raise ValueError("Parameter n must be greater than or equal to one.") From 740e851f2ea3eb4d60ec97af33016d83f65b3f57 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Fri, 22 May 2026 11:38:12 +0000 Subject: [PATCH 09/14] use Python 3 exception syntax in PE problem_005 and problem_007 --- project_euler/problem_005/sol1.py | 2 +- project_euler/problem_007/sol2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_005/sol1.py b/project_euler/problem_005/sol1.py index f889c420c61d..01cbd0e15ff7 100644 --- a/project_euler/problem_005/sol1.py +++ b/project_euler/problem_005/sol1.py @@ -47,7 +47,7 @@ def solution(n: int = 20) -> int: try: n = int(n) - except TypeError, ValueError: + except (TypeError, ValueError): raise TypeError("Parameter n must be int or castable to int.") if n <= 0: raise ValueError("Parameter n must be greater than or equal to one.") diff --git a/project_euler/problem_007/sol2.py b/project_euler/problem_007/sol2.py index d63b2f2d86ec..fd99453c1100 100644 --- a/project_euler/problem_007/sol2.py +++ b/project_euler/problem_007/sol2.py @@ -87,7 +87,7 @@ def solution(nth: int = 10001) -> int: try: nth = int(nth) - except TypeError, ValueError: + except (TypeError, ValueError): raise TypeError("Parameter nth must be int or castable to int.") from None if nth <= 0: raise ValueError("Parameter nth must be greater than or equal to one.") From 9dd973acf14beb30ffe247d504620ec527db14ac Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Fri, 22 May 2026 11:39:44 +0000 Subject: [PATCH 10/14] use Python 3 exception syntax in fetch_well_rx_price --- web_programming/fetch_well_rx_price.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_programming/fetch_well_rx_price.py b/web_programming/fetch_well_rx_price.py index 680d7444bd1c..e34a89c19cc8 100644 --- a/web_programming/fetch_well_rx_price.py +++ b/web_programming/fetch_well_rx_price.py @@ -67,7 +67,7 @@ def fetch_pharmacy_and_price_list(drug_name: str, zip_code: str) -> list | None: return pharmacy_price_list - except httpx.HTTPError, ValueError: + except (httpx.HTTPError, ValueError): return None From 91774602e40edced610d9b6009129a781d434ca8 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Sun, 7 Jun 2026 17:31:18 +0000 Subject: [PATCH 11/14] =?UTF-8?q?narrow=20broad=20except=20in=20newtons=5F?= =?UTF-8?q?second=5Flaw=5Fof=5Fmotion=20to=20TypeError=20=E2=80=94=20float?= =?UTF-8?q?*float=20can=20only=20raise=20TypeError=20for=20incompatible=20?= =?UTF-8?q?types;=20the=20bare=20Exception=20was=20masking=20real=20bugs?= =?UTF-8?q?=20from=20the=20caller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- physics/newtons_second_law_of_motion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/newtons_second_law_of_motion.py b/physics/newtons_second_law_of_motion.py index 4149e2494f31..74cb3bcda703 100644 --- a/physics/newtons_second_law_of_motion.py +++ b/physics/newtons_second_law_of_motion.py @@ -74,7 +74,7 @@ def newtons_second_law_of_motion(mass: float, acceleration: float) -> float: force = 0.0 try: force = mass * acceleration - except Exception: + except TypeError: return -0.0 return force From 118197bb589089fa863692e0f94755b495bd8004 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Fri, 22 May 2026 01:32:56 +0000 Subject: [PATCH 12/14] display the computed negative instead of the original image in convert_to_negative --- digital_image_processing/convert_to_negative.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/digital_image_processing/convert_to_negative.py b/digital_image_processing/convert_to_negative.py index 9bf2d8f2c075..227aab51bc54 100644 --- a/digital_image_processing/convert_to_negative.py +++ b/digital_image_processing/convert_to_negative.py @@ -25,6 +25,6 @@ def convert_to_negative(img): neg = convert_to_negative(img) # show result image - imshow("negative of original image", img) + imshow("negative of original image", neg) waitKey(0) destroyAllWindows() From 72fac6f65a6d8c3f956f1873f11654d6a98324a5 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Tue, 9 Jun 2026 23:28:23 +0000 Subject: [PATCH 13/14] narrow TypeError guard in newtons_second_law_of_motion The bare 'except Exception' around mass * acceleration was catching RecursionError, MemoryError, KeyboardInterrupt, and any unrelated bug in surrounding code. The multiplication of two Python objects can only raise TypeError (wrong operand types) or OverflowError (very large floats), so narrowing to TypeError matches the documented error path and lets other exceptions propagate as designed. Also adds a doctest that exercises the error path with a None argument, so the new behavior is locked in by the test suite. --- physics/newtons_second_law_of_motion.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/physics/newtons_second_law_of_motion.py b/physics/newtons_second_law_of_motion.py index 74cb3bcda703..58e18dffcdd0 100644 --- a/physics/newtons_second_law_of_motion.py +++ b/physics/newtons_second_law_of_motion.py @@ -70,6 +70,8 @@ def newtons_second_law_of_motion(mass: float, acceleration: float) -> float: 100 >>> newtons_second_law_of_motion(2.0, 1) 2.0 + >>> newtons_second_law_of_motion(None, 10) + -0.0 """ force = 0.0 try: From 1c8a3bb1c3b9198e156aeaf0856ffb62e0359ad5 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Wed, 10 Jun 2026 07:38:21 +0000 Subject: [PATCH 14/14] reject bool and float as polygon sides; cover negative-breadth cuboid Two real bugs in maths/area.py: 1. area_reg_polygon()'s isinstance(sides, int) check was passing for bool, because bool is a subclass of int in Python. area_reg_polygon(True, 5) would silently compute cot(pi/1) for a '1-sided regular polygon' and return a real number, when the function is meant to reject anything that is not a genuine integer count of sides >= 3. Added an explicit isinstance(sides, bool) check and a docstring note explaining the subclass relationship. Also split the original so the type check on sides and the non-negative check on length are no longer mutually exclusive branches. Previously, sides=True and length=-1 would silently pass the type guard, then hit the elif and raise the wrong error. Added doctest cases for area_reg_polygon(True, 5) and area_reg_polygon(3.0, 5) so the new behavior is locked in. 2. surface_area_cuboid()'s existing test coverage happened to miss triples where the negative argument is the *middle* one (length=1, breadth=-1, height=1). Added a doctest case for that exact input. The if-statement itself was already correct (short- circuit-or with three comparands binds tighter than the not-yet-existing precedence issue), but the missing test meant nothing was actually exercising the breadth < 0 branch in isolation. The new case locks it in. Verified: `python3 -m doctest maths/area.py -v` reports '116 passed and 0 failed'. --- maths/area.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/maths/area.py b/maths/area.py index e14cc0aa7195..5bccc74ee899 100644 --- a/maths/area.py +++ b/maths/area.py @@ -50,6 +50,10 @@ def surface_area_cuboid(length: float, breadth: float, height: float) -> float: Traceback (most recent call last): ... ValueError: surface_area_cuboid() only accepts non-negative values + >>> surface_area_cuboid(1, -1, 1) + Traceback (most recent call last): + ... + ValueError: surface_area_cuboid() only accepts non-negative values """ if length < 0 or breadth < 0 or height < 0: raise ValueError("surface_area_cuboid() only accepts non-negative values") @@ -539,14 +543,34 @@ def area_reg_polygon(sides: int, length: float) -> float: Traceback (most recent call last): ... ValueError: area_reg_polygon() only accepts integers greater than or equal to \ +three as number of sides + >>> area_reg_polygon(True, 5) + Traceback (most recent call last): + ... + ValueError: area_reg_polygon() only accepts integers greater than or equal to \ +three as number of sides + >>> area_reg_polygon(3.0, 5) + Traceback (most recent call last): + ... + ValueError: area_reg_polygon() only accepts integers greater than or equal to \ three as number of sides """ - if not isinstance(sides, int) or sides < 3: + # bool is a subclass of int in Python, so we must reject it explicitly. + # Without this, area_reg_polygon(True, 5) would silently pass the + # isinstance(sides, int) check (because True IS an int) and compute + # cot(pi/1) for a "1-sided regular polygon", which is geometrically + # meaningless but mathematically defined. A count of polygon sides + # should never be a bool. + if ( + not isinstance(sides, int) + or isinstance(sides, bool) + or sides < 3 + ): raise ValueError( "area_reg_polygon() only accepts integers greater than or \ equal to three as number of sides" ) - elif length < 0: + if length < 0: raise ValueError( "area_reg_polygon() only accepts non-negative values as \ length of a side"