-
Notifications
You must be signed in to change notification settings - Fork 9
Implement Git-mastery REPL #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
375c824
c3374dd
1800b04
259d728
33fa0f3
43f8537
a39b814
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,3 +176,6 @@ cython_debug/ | |
| gitmastery-exercises/ | ||
|
|
||
| Gemfile.lock | ||
|
|
||
| # AI settings | ||
| .claude/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,9 @@ | ||
| __all__ = ["check", "download", "progress", "setup", "verify", "version"] | ||
| __all__ = ["check", "download", "progress", "repl", "setup", "verify", "version"] | ||
|
|
||
| from .check import check | ||
| from .download import download | ||
| from .progress.progress import progress | ||
| from .repl import repl | ||
| from .setup_folder import setup | ||
| from .verify import verify | ||
| from .version import version |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,223 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import cmd | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import shlex | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import subprocess | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import List | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import click | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.commands.check import check | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.commands.download import download | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.commands.progress.progress import progress | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.commands.setup_folder import setup | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.commands.verify import verify | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.commands.version import version | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.utils.click import CliContextKey, ClickColor | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.utils.version import Version | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.version import __version__ | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| GITMASTERY_COMMANDS = { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "check": check, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "download": download, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "progress": progress, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "setup": setup, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "verify": verify, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "version": version, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| class GitMasteryREPL(cmd.Cmd): | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Interactive REPL for Git-Mastery commands.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| intro_msg = r""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| _____ _ _ ___ ___ _ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| | __ (_) | | \/ | | | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| | | \/_| |_| . . | __ _ ___| |_ ___ _ __ _ _ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| | | __| | __| |\/| |/ _` / __| __/ _ \ '__| | | | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| | |_\ \ | |_| | | | (_| \__ \ || __/ | | |_| | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| \____/_|\__\_| |_/\__,_|___/\__\___|_| \__, | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| __/ | | ||||||||||||||||||||||||||||||||||||||||||||||||||
| |___/ | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| Welcome to the Git-Mastery REPL! | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Type 'help' for available commands, or 'exit' to quit. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Git-Mastery commands work with or without the 'gitmastery' prefix. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Shell commands are also supported. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| intro = click.style( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| intro_msg, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| bold=True, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fg=ClickColor.BRIGHT_CYAN, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def __init__(self) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| super().__init__() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| self._update_prompt() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def _update_prompt(self) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Update prompt to show current directory.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd = os.path.basename(os.getcwd()) or os.getcwd() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| self.prompt = click.style(f"gitmastery [{cwd}]> ", fg=ClickColor.BRIGHT_GREEN) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def postcmd(self, stop: bool, line: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Update prompt after each command.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| self._update_prompt() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return stop | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def precmd(self, line: str) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Pre-process command line before execution.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stripped = line.strip() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if stripped.lower().startswith("gitmastery "): | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return stripped[len("gitmastery ") :].lstrip() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return line | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def default(self, line: str) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Handle commands not recognized by cmd module.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| parts = shlex.split(line) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo(click.style(f"Input error: {e}", fg=ClickColor.BRIGHT_RED)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if not parts: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| command_name = parts[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| args = parts[1:] | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if command_name in GITMASTERY_COMMANDS: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| self._run_gitmastery_command(command_name, args) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| self._run_shell_command(line) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def _run_gitmastery_command(self, command_name: str, args: List[str]) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Execute a gitmastery command.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| command = GITMASTERY_COMMANDS[command_name] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| original_cwd = os.getcwd() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx = command.make_context(command_name, args) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.ensure_object(dict) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.obj[CliContextKey.VERBOSE] = False | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.obj[CliContextKey.VERSION] = Version.parse_version_string(__version__) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| with ctx: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| command.invoke(ctx) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except click.ClickException as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.show() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except click.Abort: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo("Aborted.") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except SystemExit: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo(click.style(f"Error: {e}", fg=ClickColor.BRIGHT_RED)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| os.chdir(original_cwd) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except (FileNotFoundError, PermissionError, OSError) as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.style( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Warning: Could not restore original directory: {e}", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fg=ClickColor.BRIGHT_YELLOW, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def _run_shell_command(self, line: str) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Execute a shell command via subprocess.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| result = subprocess.run(line, shell=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if result.returncode != 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.style( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Command exited with code {result.returncode}", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fg=ClickColor.BRIGHT_YELLOW, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo(click.style(f"Shell error: {e}", fg=ClickColor.BRIGHT_RED)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def do_cd(self, path: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Change directory.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if not path: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| path = os.path.expanduser("~") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| parts = shlex.split(path) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| path = parts[0] if parts else "" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||
| 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) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+151
to
+160
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 When a user runs Root Cause
Notably, the except (FileNotFoundError, PermissionError, OSError) as e:but Impact: Any
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||||||||||||||||||||||||
| except OSError as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.style(f"Cannot change directory: {e}", fg=ClickColor.BRIGHT_RED) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def do_exit(self, arg: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Exit the Git-Mastery REPL.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo(click.style("Goodbye!", fg=ClickColor.BRIGHT_CYAN)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return True | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def do_quit(self, arg: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Exit the Git-Mastery REPL.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return self.do_exit(arg) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def do_help(self, arg: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Show help for commands.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.style("\nGit-Mastery Commands:", bold=True, fg=ClickColor.BRIGHT_CYAN) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| for name, command in GITMASTERY_COMMANDS.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||
| help_text = (command.help or "No description available.").strip() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo(f" {click.style(f'{name:<20}', bold=True)} {help_text}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.style("\nBuilt-in Commands:", bold=True, fg=ClickColor.BRIGHT_CYAN) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| for name, desc in [ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ("help", "Show this help message"), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ("exit", "Exit the REPL"), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ("quit", "Exit the REPL"), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ]: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo(f" {click.style(f'{name:<20}', bold=True)} {desc}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.style( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "\nAll other commands are passed to the shell.", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fg=ClickColor.BRIGHT_YELLOW, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def emptyline(self) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Do nothing on empty line (don't repeat last command).""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| def do_EOF(self, arg: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Handle Ctrl+D.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return self.do_exit(arg) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| @click.command() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| def repl() -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| """Start an interactive REPL session.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| repl_instance = GitMasteryREPL() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| repl_instance.cmdloop() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| except KeyboardInterrupt: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| click.echo(click.style("\nInterrupted. Goodbye!", fg=ClickColor.BRIGHT_CYAN)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| sys.exit(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -33,6 +33,21 @@ | |||||||||||||||||||||
| ] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def _clear_exercise_utils_modules() -> None: | ||||||||||||||||||||||
| """Clear cached exercise_utils modules from sys.modules. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| This is especially important in REPL context where modules persist | ||||||||||||||||||||||
| between command invocations. | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| modules_to_remove = [ | ||||||||||||||||||||||
| key | ||||||||||||||||||||||
| for key in sys.modules | ||||||||||||||||||||||
| if key == "exercise_utils" or key.startswith("exercise_utils.") | ||||||||||||||||||||||
| ] | ||||||||||||||||||||||
| for mod in modules_to_remove: | ||||||||||||||||||||||
| del sys.modules[mod] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class ExercisesRepo: | ||||||||||||||||||||||
| def __init__(self) -> None: | ||||||||||||||||||||||
| """Creates a sparse clone of the exercises repository. | ||||||||||||||||||||||
|
|
@@ -126,6 +141,9 @@ def load_file_as_namespace( | |||||||||||||||||||||
| py_file = exercises_repo.fetch_file_contents(file_path, False) | ||||||||||||||||||||||
| namespace: Dict[str, Any] = {} | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Clear any cached exercise_utils modules to ensure fresh imports | ||||||||||||||||||||||
| _clear_exercise_utils_modules() | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| with tempfile.TemporaryDirectory() as tmpdir: | ||||||||||||||||||||||
| package_root = os.path.join(tmpdir, "exercise_utils") | ||||||||||||||||||||||
| os.makedirs(package_root, exist_ok=True) | ||||||||||||||||||||||
|
|
@@ -142,8 +160,9 @@ def load_file_as_namespace( | |||||||||||||||||||||
| exec(py_file, namespace) | ||||||||||||||||||||||
| finally: | ||||||||||||||||||||||
| sys.path.remove(tmpdir) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| sys.dont_write_bytecode = False | ||||||||||||||||||||||
| # Clean up cached modules again after execution | ||||||||||||||||||||||
| _clear_exercise_utils_modules() | ||||||||||||||||||||||
|
Comment on lines
161
to
+164
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 If Root CauseThe PR correctly added In the normal CLI flow this was harmless because the process exits after each command. But now with the REPL ( Impact: After a single
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||
| sys.dont_write_bytecode = False | ||||||||||||||||||||||
| return cls(namespace) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def execute_function( | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.