Skip to content

narrow TypeError guard in newtons_second_law_of_motion#3

Open
HrachShah wants to merge 12 commits into
masterfrom
fix/newtons-second-law-broad-exception
Open

narrow TypeError guard in newtons_second_law_of_motion#3
HrachShah wants to merge 12 commits into
masterfrom
fix/newtons-second-law-broad-exception

Conversation

@HrachShah

@HrachShah HrachShah commented Jun 9, 2026

Copy link
Copy Markdown
Owner

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.

Summary by Sourcery

Align several modules with modern Python error-handling and I/O practices while correcting logic errors in algorithms and examples.

New Features:

  • Add a doctest for newtons_second_law_of_motion to validate behavior when given an invalid (None) argument.

Bug Fixes:

  • Fix depth-first search traversal to track visited vertices by label and correctly recurse over adjacency lists.
  • Correct the image negative example to display the processed negative image instead of the original.

Enhancements:

  • Narrow overly broad exception handling in newtons_second_law_of_motion and other modules to catch only the relevant exception types.
  • Refactor file handling in Project Euler problem 99 solution to use a context manager for reading the data file.
  • Update multiple legacy except clauses to modern tuple-based syntax for cleaner and more accurate error handling.

Tests:

  • Extend doctest coverage for newtons_second_law_of_motion to cover the TypeError error path.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed negative image display in image conversion utility
  • Refactor

    • Updated exception handling for Python 3 compatibility across modules
    • Optimized graph traversal algorithm to use more efficient data structures
    • Improved file I/O handling with proper resource management

Zo Bot added 12 commits May 6, 2026 15:34
…ix Python 3 multi-exception syntax in instagram_crawler
…xception — int() raises ValueError on non-numeric input, not a generic Exception
…emoved in Python 3, use parentheses instead
…oat*float can only raise TypeError for incompatible types; the bare Exception was masking real bugs from the caller
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.
@sourcery-ai

sourcery-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR narrows overly broad exception handling in multiple modules (notably in newtons_second_law_of_motion) to specific exception types, updates a DFS implementation to use vertex-keyed visitation with correct adjacency iteration, fixes a bug in an image negative demo, makes file handling in a Project Euler solution safer, and adds a doctest to lock in the TypeError behavior when invalid arguments are passed to the physics function.

Flow diagram for newtons_second_law_of_motion error handling

graph TD
    A[newtons_second_law_of_motion called<br/>mass, acceleration] --> B[force = 0.0]
    B --> C[Attempt force = mass * acceleration]
    C --> D{TypeError raised?}
    D -- Yes --> E[Return -0.0]
    D -- No --> F[Return computed force]
Loading

File-Level Changes

Change Details Files
Refine DFS implementation to track visits by vertex label and iterate over actual neighbors
  • Initialize visited as a dict keyed by vertex values instead of an index-based list
  • Update dfs to iterate directly over vertices and call dfs_recursive on unvisited ones
  • Change dfs_recursive signature to accept a dict for visited
  • Update doctest setup to build the visited dict from graph vertices
  • Iterate over self.vertex[start_vertex] as neighbors and recurse only on unvisited neighbors
graphs/depth_first_search_2.py
Make Project Euler problem 99 file handling safer and more idiomatic
  • Replace direct open call in for-loop with a context-managed open using with
  • Iterate over the opened file handle instead of repeatedly calling open
project_euler/problem_099/sol1.py
Narrow exception handling in Newton's second law implementation and add doctest for error path
  • Restrict the try/except around mass * acceleration to catch only TypeError
  • Add doctest exercising the None mass case that should return -0.0
physics/newtons_second_law_of_motion.py
Fix display bug in image negative conversion example
  • Change imshow to display the negative image result instead of the original input image
digital_image_processing/convert_to_negative.py
Modernize and correct exception handling syntax across several modules
  • Change legacy except IndexError, TypeError syntax to except (IndexError, TypeError) in convex_hull
  • Change legacy except NameError, ValueError syntax to except (NameError, ValueError) in catalan_numbers and other scripts
  • Restrict broad except Exception when parsing integer token in enigma_machine to except ValueError
  • Unify int-casting guards in several Project Euler solutions to use tuple exception syntax e.g. except (TypeError, ValueError)
  • Wrap HTTP and value errors in fetch_well_rx_price with tuple exception syntax
  • Use tuple exception syntax for JSONDecodeError and KeyError in instagram_crawler
divide_and_conquer/convex_hull.py
dynamic_programming/catalan_numbers.py
hashes/enigma_machine.py
project_euler/problem_002/sol4.py
project_euler/problem_003/sol1.py
project_euler/problem_003/sol2.py
project_euler/problem_003/sol3.py
project_euler/problem_005/sol1.py
project_euler/problem_007/sol2.py
web_programming/fetch_well_rx_price.py
web_programming/instagram_crawler.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR systematically modernizes Python 2-style code to Python 3 standards across 16 files, including exception handler syntax updates, refined exception type catching, graph traversal data structure refactoring, resource management improvements, and a display logic fix.

Changes

Python 3 Compatibility and Code Modernization

Layer / File(s) Summary
Python 3 exception syntax migration
divide_and_conquer/convex_hull.py, dynamic_programming/catalan_numbers.py, project_euler/problem_002/sol4.py, project_euler/problem_003/sol1.py, project_euler/problem_003/sol2.py, project_euler/problem_003/sol3.py, project_euler/problem_005/sol1.py, project_euler/problem_007/sol2.py
Exception handlers across 8 files are updated from Python 2's comma-separated syntax (except A, B:) to Python 3's tuple syntax (except (A, B):) for multi-exception catching.
Exception type narrowing for targeted handling
hashes/enigma_machine.py, physics/newtons_second_law_of_motion.py
Error handling is refined to catch only specific exception types (ValueError and TypeError respectively) instead of generic Exception, ensuring only expected failures are handled and unexpected errors propagate.
DFS graph traversal visited state refactor
graphs/depth_first_search_2.py
The dfs() and dfs_recursive() methods switch from list-based visited tracking ([False] * len(self.vertex)) to dict-based tracking ({v: False for v in self.vertex}), with updated type hints, neighbor iteration logic, and docstring examples.
File I/O context manager and display output fix
project_euler/problem_099/sol1.py, web_programming/fetch_well_rx_price.py, web_programming/instagram_crawler.py, digital_image_processing/convert_to_negative.py
File reading adopts context manager pattern for proper resource cleanup; web module exception handlers use Python 3 syntax for httpx and JSON errors; image display is corrected to show the computed negative instead of the original.

Sequence Diagram(s)

No sequence diagram is necessary for this PR, as the changes consist of syntax updates, exception handling refinement, data structure refactoring, and resource management improvements without introducing multi-component interactions or altered control flow that would benefit from visualization.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A Python that's modern, from two-thousand-seven,
Exception syntax now arranged in a tuple heaven!
Dict-tracked vertices, context managers tight,
Old code made bright with Python Three's light!
With patience and care, this codebase takes flight! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the rationale for the change and mentions adding a doctest, but lacks most required template sections like change categorization, compliance checklist items, and confirmation of PR scope. Complete the PR template by selecting appropriate checkboxes (e.g., 'Fix a bug'), confirming compliance with CONTRIBUTING.md, and verifying this PR only modifies one algorithm file.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: narrowing a TypeError guard in the newtons_second_law_of_motion function. It directly relates to the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/newtons-second-law-broad-exception

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In graphs/depth_first_search_2.py, visited = {v: False for v in self.vertex} inside dfs() will use the adjacency-list entries themselves as keys (likely lists), which are unhashable; you probably want keys to be the vertex indices (e.g., range(len(self.vertex))) to match how dfs_recursive indexes self.vertex[start_vertex] and visited[neighbor].
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `graphs/depth_first_search_2.py`, `visited = {v: False for v in self.vertex}` inside `dfs()` will use the adjacency-list entries themselves as keys (likely lists), which are unhashable; you probably want keys to be the vertex indices (e.g., `range(len(self.vertex))`) to match how `dfs_recursive` indexes `self.vertex[start_vertex]` and `visited[neighbor]`.

## Individual Comments

### Comment 1
<location path="physics/newtons_second_law_of_motion.py" line_range="73-74" />
<code_context>
     100
     >>> newtons_second_law_of_motion(2.0, 1)
     2.0
+    >>> newtons_second_law_of_motion(None, 10)
+    -0.0
     """
     force = 0.0
</code_context>
<issue_to_address>
**question (bug_risk):** Reconsider treating `None` as a valid input that silently returns `-0.0`.

This doctest effectively endorses `newtons_second_law_of_motion(None, 10)` returning `-0.0` as a normal result, conflating an invalid-parameter/sentinel condition with a legitimately possible physics value. That makes it difficult for callers to distinguish bad inputs from real results. Consider instead raising `TypeError`/`ValueError` for non-numeric `mass`/`acceleration`, or clearly documenting any sentinel behavior rather than folding it into the standard API.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +73 to +74
>>> newtons_second_law_of_motion(None, 10)
-0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): Reconsider treating None as a valid input that silently returns -0.0.

This doctest effectively endorses newtons_second_law_of_motion(None, 10) returning -0.0 as a normal result, conflating an invalid-parameter/sentinel condition with a legitimately possible physics value. That makes it difficult for callers to distinguish bad inputs from real results. Consider instead raising TypeError/ValueError for non-numeric mass/acceleration, or clearly documenting any sentinel behavior rather than folding it into the standard API.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
graphs/depth_first_search_2.py (1)

79-85: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the visited docstring to match the new contract.

The signature now takes a dict, but the parameter docs still say "A list to track visited vertices." That leaves the public API docs stale.

Suggested fix
-        :param visited: A list to track visited vertices.
+        :param visited: A dict keyed by vertex ID to track visited vertices.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@graphs/depth_first_search_2.py` around lines 79 - 85, The docstring for
dfs_recursive is out of date: the parameter visited is now a dict but the docs
say "A list"; update the visited param description in the dfs_recursive
docstring to describe the dict contract (e.g., mapping vertex -> bool or any
marker of visited state), including expected key/value types and whether keys
are added by the function, so the public API for dfs_recursive (method name
dfs_recursive) accurately documents the visited: dict parameter.
🧹 Nitpick comments (1)
physics/newtons_second_law_of_motion.py (1)

65-75: ⚡ Quick win

Type hints don't reflect actual behavior.

The type signature declares mass: float, but the new doctest and error handling demonstrate that None is accepted (returning -0.0). Type checkers will flag calls like newtons_second_law_of_motion(None, 10) as invalid, even though the runtime accepts it.

Consider either:

  1. Tightening validation to reject non-float inputs and raise a descriptive error, or
  2. Updating type hints to mass: float | None, acceleration: float | None if None is a valid input

Additionally, using -0.0 as a sentinel value for invalid input is non-idiomatic and error-prone—callers can't reliably distinguish it from a legitimate zero-force result. Raising a TypeError or ValueError with a clear message would be more explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@physics/newtons_second_law_of_motion.py` around lines 65 - 75, The function
newtons_second_law_of_motion currently accepts None and returns -0.0; instead,
tighten input validation by keeping the signature as mass: float, acceleration:
float and adding explicit runtime checks at the top of
newtons_second_law_of_motion to ensure mass and acceleration are numbers (int or
float) and not None, raising a TypeError with a clear message like "mass must be
a number" or "acceleration must be a number" when validation fails; remove the
-0.0 sentinel behavior and update doctests to expect the TypeError for invalid
inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@graphs/depth_first_search_2.py`:
- Around line 74-77: The issue is that add_edge() only ensures from_vertex
exists in self.vertex, so edges like add_edge(0, 1) leave sink vertices
unregistered and later DFS (in dfs_recursive or the traversal loop that builds
visited) raises KeyError; update add_edge(from_vertex, to_vertex) to also
register to_vertex in self.vertex (e.g., ensure self.vertex[to_vertex] exists
with an empty neighbor list or appropriate default) so all vertices referenced
by edges are materialized before dfs_recursive and the visited dict is
constructed.
- Line 74: Replace the dict comprehension that builds the visited map with
dict.fromkeys to satisfy Ruff C420: where visited is currently created as
"visited = {v: False for v in self.vertex}", change it to use
"dict.fromkeys(self.vertex, False)" (keep the same variable name `visited` and
same use of `self.vertex`) so the DFS function/method that references `visited`
continues to work identically while unblocking the linter.

---

Outside diff comments:
In `@graphs/depth_first_search_2.py`:
- Around line 79-85: The docstring for dfs_recursive is out of date: the
parameter visited is now a dict but the docs say "A list"; update the visited
param description in the dfs_recursive docstring to describe the dict contract
(e.g., mapping vertex -> bool or any marker of visited state), including
expected key/value types and whether keys are added by the function, so the
public API for dfs_recursive (method name dfs_recursive) accurately documents
the visited: dict parameter.

---

Nitpick comments:
In `@physics/newtons_second_law_of_motion.py`:
- Around line 65-75: The function newtons_second_law_of_motion currently accepts
None and returns -0.0; instead, tighten input validation by keeping the
signature as mass: float, acceleration: float and adding explicit runtime checks
at the top of newtons_second_law_of_motion to ensure mass and acceleration are
numbers (int or float) and not None, raising a TypeError with a clear message
like "mass must be a number" or "acceleration must be a number" when validation
fails; remove the -0.0 sentinel behavior and update doctests to expect the
TypeError for invalid inputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6bc711be-3b2b-4df5-9dcf-441ea3275e2c

📥 Commits

Reviewing files that changed from the base of the PR and between 234e0e7 and 72fac6f.

📒 Files selected for processing (15)
  • digital_image_processing/convert_to_negative.py
  • divide_and_conquer/convex_hull.py
  • dynamic_programming/catalan_numbers.py
  • graphs/depth_first_search_2.py
  • hashes/enigma_machine.py
  • physics/newtons_second_law_of_motion.py
  • project_euler/problem_002/sol4.py
  • project_euler/problem_003/sol1.py
  • project_euler/problem_003/sol2.py
  • project_euler/problem_003/sol3.py
  • project_euler/problem_005/sol1.py
  • project_euler/problem_007/sol2.py
  • project_euler/problem_099/sol1.py
  • web_programming/fetch_well_rx_price.py
  • web_programming/instagram_crawler.py

"""
# visited array for storing already visited nodes
visited = [False] * len(self.vertex)
visited = {v: False for v in self.vertex}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use dict.fromkeys() to unblock Ruff.

Line 74 is the exact C420 failure from CI. dict.fromkeys(self.vertex, False) is equivalent here and satisfies Ruff.

Suggested fix
-        visited = {v: False for v in self.vertex}
+        visited = dict.fromkeys(self.vertex, False)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
visited = {v: False for v in self.vertex}
visited = dict.fromkeys(self.vertex, False)
🧰 Tools
🪛 GitHub Actions: ruff / 0_ruff.txt

[error] 74-74: ruff check failed: C420 Unnecessary dict comprehension for iterable; use dict.fromkeys instead

🪛 GitHub Actions: ruff / ruff

[error] 74-74: ruff check failed (C420): Unnecessary dict comprehension for iterable; use dict.fromkeys instead

🪛 GitHub Check: ruff

[failure] 74-74: ruff (C420)
graphs/depth_first_search_2.py:74:19: C420 Unnecessary dict comprehension for iterable; use dict.fromkeys instead
help: Replace with dict.fromkeys(iterable))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@graphs/depth_first_search_2.py` at line 74, Replace the dict comprehension
that builds the visited map with dict.fromkeys to satisfy Ruff C420: where
visited is currently created as "visited = {v: False for v in self.vertex}",
change it to use "dict.fromkeys(self.vertex, False)" (keep the same variable
name `visited` and same use of `self.vertex`) so the DFS function/method that
references `visited` continues to work identically while unblocking the linter.

Sources: Linters/SAST tools, Pipeline failures

Comment on lines +74 to +77
visited = {v: False for v in self.vertex}
for vertex in self.vertex:
if not visited[vertex]:
self.dfs_recursive(vertex, visited)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Register sink vertices before traversing.

This refactor still assumes every neighbor is already a key in self.vertex, but add_edge() only inserts from_vertex. A valid graph like g.add_edge(0, 1); g.dfs() will hit visited[1] / self.vertex[1] and raise KeyError because vertex 1 was never materialized. The fix belongs in add_edge(), not in a defensive .get() here.

Suggested fix
 def add_edge(self, from_vertex: int, to_vertex: int) -> None:
-        if from_vertex in self.vertex:
-            self.vertex[from_vertex].append(to_vertex)
-        else:
-            # else make a new vertex
-            self.vertex[from_vertex] = [to_vertex]
+        self.vertex.setdefault(from_vertex, []).append(to_vertex)
+        self.vertex.setdefault(to_vertex, [])

Also applies to: 104-107

🧰 Tools
🪛 GitHub Actions: ruff / 0_ruff.txt

[error] 74-74: ruff check failed: C420 Unnecessary dict comprehension for iterable; use dict.fromkeys instead

🪛 GitHub Actions: ruff / ruff

[error] 74-74: ruff check failed (C420): Unnecessary dict comprehension for iterable; use dict.fromkeys instead

🪛 GitHub Check: ruff

[failure] 74-74: ruff (C420)
graphs/depth_first_search_2.py:74:19: C420 Unnecessary dict comprehension for iterable; use dict.fromkeys instead
help: Replace with dict.fromkeys(iterable))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@graphs/depth_first_search_2.py` around lines 74 - 77, The issue is that
add_edge() only ensures from_vertex exists in self.vertex, so edges like
add_edge(0, 1) leave sink vertices unregistered and later DFS (in dfs_recursive
or the traversal loop that builds visited) raises KeyError; update
add_edge(from_vertex, to_vertex) to also register to_vertex in self.vertex
(e.g., ensure self.vertex[to_vertex] exists with an empty neighbor list or
appropriate default) so all vertices referenced by edges are materialized before
dfs_recursive and the visited dict is constructed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant