Skip to content

Implement Git-mastery REPL#55

Open
jiaxinnns wants to merge 7 commits intogit-mastery:mainfrom
jiaxinnns:feat/repl
Open

Implement Git-mastery REPL#55
jiaxinnns wants to merge 7 commits intogit-mastery:mainfrom
jiaxinnns:feat/repl

Conversation

@jiaxinnns
Copy link

@jiaxinnns jiaxinnns commented Feb 17, 2026

This comment was marked as resolved.

Copy link
Collaborator

@jovnc jovnc left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +138 to +147
try:
os.chdir(os.path.expanduser(path))
except FileNotFoundError:
click.echo(
click.style(f"Directory not found: {path}", fg=ClickColor.BRIGHT_RED)
)
except PermissionError:
click.echo(
click.style(f"Permission denied: {path}", fg=ClickColor.BRIGHT_RED)
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

🔴 do_cd missing NotADirectoryError/OSError handler crashes the REPL

When a user runs cd somefile.txt (pointing to a regular file, not a directory), os.chdir() raises NotADirectoryError. This exception is not caught by the except FileNotFoundError or except PermissionError handlers at app/commands/repl.py:140-147, causing it to propagate uncaught through cmd.Cmd.cmdloop() and crash the entire REPL session with a traceback.

Root Cause

NotADirectoryError is a subclass of OSError but NOT a subclass of FileNotFoundError or PermissionError. The do_cd method only catches those two specific exceptions, missing NotADirectoryError and other possible OSError subclasses.

Notably, the _run_gitmastery_command method in the same file correctly catches the broader OSError at app/commands/repl.py:106:

except (FileNotFoundError, PermissionError, OSError) as e:

but do_cd does not follow the same pattern.

Impact: Any cd to a non-directory path (e.g., a regular file) or a path triggering other OSError variants (symlink loops, etc.) will crash the REPL, forcing the user to restart their session.

Suggested change
try:
os.chdir(os.path.expanduser(path))
except FileNotFoundError:
click.echo(
click.style(f"Directory not found: {path}", fg=ClickColor.BRIGHT_RED)
)
except PermissionError:
click.echo(
click.style(f"Permission denied: {path}", fg=ClickColor.BRIGHT_RED)
)
try:
os.chdir(os.path.expanduser(path))
except FileNotFoundError:
click.echo(
click.style(f"Directory not found: {path}", fg=ClickColor.BRIGHT_RED)
)
except PermissionError:
click.echo(
click.style(f"Permission denied: {path}", fg=ClickColor.BRIGHT_RED)
)
except OSError as e:
click.echo(
click.style(f"Cannot change directory: {e}", fg=ClickColor.BRIGHT_RED)
)
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 161 to +164
finally:
sys.path.remove(tmpdir)
# Clean up cached modules again after execution
_clear_exercise_utils_modules()
Copy link
Collaborator

Choose a reason for hiding this comment

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

🟡 sys.dont_write_bytecode not reset in finally block, stays True for entire REPL session on failure

If exec() at app/utils/gitmastery.py:160 raises an exception, the finally block at lines 161-164 cleans up sys.path and cached modules, but sys.dont_write_bytecode = False at line 166 is outside the try/finally and is never reached. In the REPL context (where the process persists across commands), this leaves sys.dont_write_bytecode = True for the remainder of the session.

Root Cause

The PR correctly added _clear_exercise_utils_modules() inside the finally block at line 164 to handle REPL persistence, but didn't move sys.dont_write_bytecode = False (line 166) into the same finally block. When exec(py_file, namespace) at line 160 throws, the exception propagates through the with tempfile.TemporaryDirectory() context and skips line 166 entirely.

In the normal CLI flow this was harmless because the process exits after each command. But now with the REPL (app/commands/repl.py), the process persists. A failed verify or download command that triggers this path will leave sys.dont_write_bytecode = True, suppressing .pyc generation for all subsequent operations in the session.

Impact: After a single load_file_as_namespace failure in the REPL, Python bytecode caching is disabled for the rest of the session, degrading performance for all subsequent imports.

Suggested change
finally:
sys.path.remove(tmpdir)
# Clean up cached modules again after execution
_clear_exercise_utils_modules()
finally:
sys.path.remove(tmpdir)
# Clean up cached modules again after execution
_clear_exercise_utils_modules()
sys.dont_write_bytecode = False
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


def do_help(self, arg: str) -> bool: # type: ignore[override]
"""Show help for commands."""
if arg:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feature is not made known to users, seems like it can show help message for specific command.

super().do_help(arg)
return False

# Show general help
Copy link
Collaborator

Choose a reason for hiding this comment

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

Image

We can fix the spacing and indentation to make it consistent

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.

REPL for Git-Mastery

3 participants