Skip to content

Conversation

@misrasaurabh1
Copy link
Contributor

@misrasaurabh1 misrasaurabh1 commented May 15, 2025

PR Type

Enhancement, Documentation


Description

  • Bump setup-uv action version to v6

  • Update docs with setup-uv@v6 reference

  • Refactor list and call formatting

  • Insert blank lines for readability


Changes walkthrough 📝

Relevant files
Enhancement
cmd_init.py
Upgrade setup-uv to v6 and refactor code formatting           

codeflash/cli_cmds/cmd_init.py

  • Upgrade GitHub action astral-sh/setup-uv to v6
  • Convert ignore_subdirs list to multiline format
  • Break long function calls into multiple lines
  • Add blank lines for readability
  • +27/-11 
    Documentation
    codeflash-github-actions.md
    Update setup-uv version in docs                                                   

    docs/docs/getting-started/codeflash-github-actions.md

  • Update example to astral-sh/setup-uv@v6
  • Align documentation with action version bump
  • +1/-1     

    Need help?
  • Type /help how to ... in the comments thread for any questions about PR-Agent usage.
  • Check out the documentation for more information.
  • @github-actions
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Dataclass Mismatch

    The SetupInfo dataclass is defined with only module_root and tests_root, but collect_setup_info returns additional fields (benchmarks_root, test_framework, ignore_paths, formatter, git_remote), causing instantiation errors.

    @dataclass(frozen=True)
    class SetupInfo:
        module_root: str
        tests_root: str
        benchmarks_root: str | None
        test_framework: str
        ignore_paths: list[str]
        formatter: str
        git_remote: str
    
    
    class DependencyManager(Enum):
        PIP = auto()
        POETRY = auto()
        UV = auto()
        UNKNOWN = auto()
    
    
    def init_codeflash() -> None:
        try:
            click.echo(f"⚡️ Welcome to Codeflash! Let's get you set up.{LF}")
    
            did_add_new_key = prompt_api_key()
    
            if should_modify_pyproject_toml():
                setup_info: SetupInfo = collect_setup_info()
    
                configure_pyproject_toml(setup_info)
    
            install_github_app()
    
            install_github_actions(override_formatter_check=True)
    
            module_string = ""
            if "setup_info" in locals():
                module_string = f" you selected ({setup_info.module_root})"
    
            click.echo(
                f"{LF}"
                f"⚡️ Codeflash is now set up! You can now run:{LF}"
                f"    codeflash --file <path-to-file> --function <function-name> to optimize a function within a file{LF}"
                f"    codeflash --file <path-to-file> to optimize all functions in a file{LF}"
                f"    codeflash --all to optimize all functions in all files in the module{module_string}{LF}"
                f"-or-{LF}"
                f"    codeflash --help to see all options{LF}"
            )
            if did_add_new_key:
                click.echo("🐚 Don't forget to restart your shell to load the CODEFLASH_API_KEY environment variable!")
                click.echo("Or run the following command to reload:")
                if os.name == "nt":
                    click.echo(f"  call {get_shell_rc_path()}")
                else:
                    click.echo(f"  source {get_shell_rc_path()}")
    
            ph("cli-installation-successful", {"did_add_new_key": did_add_new_key})
            sys.exit(0)
        except KeyboardInterrupt:
            apologize_and_exit()
    
    
    def ask_run_end_to_end_test(args: Namespace) -> None:
        from rich.prompt import Confirm
    
        run_tests = Confirm.ask(
            "⚡️ Do you want to run a sample optimization to make sure everything's set up correctly? (takes about 3 minutes)",
            choices=["y", "n"],
            default="y",
            show_choices=True,
            show_default=False,
            console=console,
        )
    
        console.rule()
    
        if run_tests:
            bubble_sort_path, bubble_sort_test_path = create_bubble_sort_file_and_test(args)
            run_end_to_end_test(args, bubble_sort_path, bubble_sort_test_path)
    
    
    def should_modify_pyproject_toml() -> bool:
        """Check if the current directory contains a valid pyproject.toml file with codeflash config
        If it does, ask the user if they want to re-configure it.
        """
        from rich.prompt import Confirm
    
        pyproject_toml_path = Path.cwd() / "pyproject.toml"
        if not pyproject_toml_path.exists():
            return True
        try:
            config, config_file_path = parse_config_file(pyproject_toml_path)
        except Exception:
            return True
    
        if "module_root" not in config or config["module_root"] is None or not Path(config["module_root"]).is_dir():
            return True
        if "tests_root" not in config or config["tests_root"] is None or not Path(config["tests_root"]).is_dir():
            return True
    
        create_toml = Confirm.ask(
            "✅ A valid Codeflash config already exists in this project. Do you want to re-configure it?",
            default=False,
            show_default=True,
        )
        return create_toml
    
    
    def collect_setup_info() -> SetupInfo:
        curdir = Path.cwd()
        # Check if the cwd is writable
        if not os.access(curdir, os.W_OK):
            click.echo(f"❌ The current directory isn't writable, please check your folder permissions and try again.{LF}")
            click.echo("It's likely you don't have write permissions for this folder.")
            sys.exit(1)
    
        # Check for the existence of pyproject.toml or setup.py
        project_name = check_for_toml_or_setup_file()
    
        ignore_subdirs = [
            "venv",
            "node_modules",
            "dist",
            "build",
            "build_temp",
            "build_scripts",
            "env",
            "logs",
            "tmp",
            "__pycache__",
        ]
        valid_subdirs = [
            d for d in next(os.walk("."))[1] if not d.startswith(".") and not d.startswith("__") and d not in ignore_subdirs
        ]
    
        valid_module_subdirs = [d for d in valid_subdirs if d != "tests"]
    
        curdir_option = f"current directory ({curdir})"
        custom_dir_option = "enter a custom directory…"
        module_subdir_options = [*valid_module_subdirs, curdir_option, custom_dir_option]
    
        module_root_answer = inquirer_wrapper(
            inquirer.list_input,
            message="Which Python module do you want me to optimize going forward? (Usually the top-most directory with "
            "all of your Python source code). Use arrow keys to select",
            choices=module_subdir_options,
            default=(project_name if project_name in module_subdir_options else module_subdir_options[0]),
        )
        if module_root_answer == curdir_option:
            module_root = "."
        elif module_root_answer == custom_dir_option:
            custom_module_root_answer = inquirer_wrapper_path(
                "path",
                message=f"Enter the path to your module directory inside {Path(curdir).resolve()}{os.path.sep} ",
                path_type=inquirer.Path.DIRECTORY,
            )
            if custom_module_root_answer:
                module_root = Path(curdir) / Path(custom_module_root_answer["path"])
            else:
                apologize_and_exit()
        else:
            module_root = module_root_answer
        ph("cli-project-root-provided")
    
        # Discover test directory
        default_tests_subdir = "tests"
        create_for_me_option = f"okay, create a tests{os.pathsep} directory for me!"
        test_subdir_options = valid_subdirs
        if "tests" not in valid_subdirs:
            test_subdir_options.append(create_for_me_option)
        custom_dir_option = "enter a custom directory…"
        test_subdir_options.append(custom_dir_option)
        tests_root_answer = inquirer_wrapper(
            inquirer.list_input,
            message="Where are your tests located? "
            f"(If you don't have any tests yet, I can create an empty tests{os.pathsep} directory for you)",
            choices=test_subdir_options,
            default=(default_tests_subdir if default_tests_subdir in test_subdir_options else test_subdir_options[0]),
        )
    
        if tests_root_answer == create_for_me_option:
            tests_root = Path(curdir) / default_tests_subdir
            tests_root.mkdir()
            click.echo(f"✅ Created directory {tests_root}{os.path.sep}{LF}")
        elif tests_root_answer == custom_dir_option:
            custom_tests_root_answer = inquirer_wrapper_path(
                "path",
                message=f"Enter the path to your tests directory inside {Path(curdir).resolve()}{os.path.sep} ",
                path_type=inquirer.Path.DIRECTORY,
            )
            if custom_tests_root_answer:
                tests_root = Path(curdir) / Path(custom_tests_root_answer["path"])
            else:
                apologize_and_exit()
        else:
            tests_root = Path(curdir) / Path(cast(str, tests_root_answer))
        tests_root = tests_root.relative_to(curdir)
        ph("cli-tests-root-provided")
    
        # Autodiscover test framework
        autodetected_test_framework = detect_test_framework(curdir, tests_root)
        autodetected_suffix = (
            f" (seems to me you're using {autodetected_test_framework})" if autodetected_test_framework else ""
        )
        test_framework = inquirer_wrapper(
            inquirer.list_input,
            message="Which test framework do you use?" + autodetected_suffix,
            choices=["pytest", "unittest"],
            default=autodetected_test_framework or "pytest",
            carousel=True,
        )
    
        ph("cli-test-framework-provided", {"test_framework": test_framework})
    
        # Get benchmarks root directory
        default_benchmarks_subdir = "benchmarks"
        create_benchmarks_option = f"okay, create a {default_benchmarks_subdir}{os.path.sep} directory for me!"
        no_benchmarks_option = "I don't need benchmarks"
    
        # Check if benchmarks directory exists inside tests directory
        tests_subdirs = []
        if tests_root.exists():
            tests_subdirs = [d.name for d in tests_root.iterdir() if d.is_dir() and not d.name.startswith(".")]
    
        benchmarks_options = []
        benchmarks_options.append(no_benchmarks_option)
        if default_benchmarks_subdir in tests_subdirs:
            benchmarks_options.append(default_benchmarks_subdir)
        benchmarks_options.extend([d for d in tests_subdirs if d != default_benchmarks_subdir and d not in ignore_subdirs])
        benchmarks_options.append(create_benchmarks_option)
        benchmarks_options.append(custom_dir_option)
    
        benchmarks_answer = inquirer_wrapper(
            inquirer.list_input,
            message="Where are your performance benchmarks located? (benchmarks must be a sub directory of your tests root directory)",
            choices=benchmarks_options,
            default=(
                default_benchmarks_subdir if default_benchmarks_subdir in benchmarks_options else benchmarks_options[0]
            ),
        )
    
        if benchmarks_answer == create_benchmarks_option:
            benchmarks_root = tests_root / default_benchmarks_subdir
            benchmarks_root.mkdir(exist_ok=True)
            click.echo(f"✅ Created directory {benchmarks_root}{os.path.sep}{LF}")
        elif benchmarks_answer == custom_dir_option:
            custom_benchmarks_answer = inquirer_wrapper_path(
                "path",
                message=f"Enter the path to your benchmarks directory inside {tests_root}{os.path.sep} ",
                path_type=inquirer.Path.DIRECTORY,
            )
            if custom_benchmarks_answer:
                benchmarks_root = tests_root / Path(custom_benchmarks_answer["path"])
            else:
                apologize_and_exit()
        elif benchmarks_answer == no_benchmarks_option:
            benchmarks_root = None
        else:
            benchmarks_root = tests_root / Path(cast(str, benchmarks_answer))
    
        # TODO: Implement other benchmark framework options
        # if benchmarks_root:
        #     benchmarks_root = benchmarks_root.relative_to(curdir)
        #
        #     # Ask about benchmark framework
        #     benchmark_framework_options = ["pytest-benchmark", "asv (Airspeed Velocity)", "custom/other"]
        #     benchmark_framework = inquirer_wrapper(
        #         inquirer.list_input,
        #         message="Which benchmark framework do you use?",
        #         choices=benchmark_framework_options,
        #         default=benchmark_framework_options[0],
        #         carousel=True,
        #     )
    
        formatter = inquirer_wrapper(
            inquirer.list_input,
            message="Which code formatter do you use?",
            choices=["black", "ruff", "other", "don't use a formatter"],
            default="black",
            carousel=True,
        )
    
        git_remote = ""
        try:
            repo = Repo(str(module_root), search_parent_directories=True)
            git_remotes = get_git_remotes(repo)
            if git_remotes:  # Only proceed if there are remotes
                if len(git_remotes) > 1:
                    git_remote = inquirer_wrapper(
                        inquirer.list_input,
                        message="What git remote do you want Codeflash to use for new Pull Requests? ",
                        choices=git_remotes,
                        default="origin",
                        carousel=True,
                    )
                else:
                    git_remote = git_remotes[0]
            else:
                click.echo(
                    "No git remotes found. You can still use Codeflash locally, but you'll need to set up a remote "
                    "repository to use GitHub features."
                )
        except InvalidGitRepositoryError:
            git_remote = ""
    
        ignore_paths: list[str] = []
        return SetupInfo(
            module_root=str(module_root),
            tests_root=str(tests_root),
            benchmarks_root=str(benchmarks_root) if benchmarks_root else None,
            test_framework=cast(str, test_framework),
            ignore_paths=ignore_paths,
            formatter=cast(str, formatter),
            git_remote=str(git_remote),

    @github-actions
    Copy link

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Fix misplaced positional string argument

    Combine the split string literals into a single message argument to avoid passing an
    unintended positional argument. You can use a parenthesized string literal to keep
    the line breaks clear.

    codeflash/cli_cmds/cmd_init.py [518-523]

     benchmark_mode = inquirer_wrapper(
         inquirer.confirm,
    -    message="⚡️It looks like you've configured a benchmarks_root in your config. Would you like to run the Github action in benchmark mode? "
    -    " This will show the impact of Codeflash's suggested optimizations on your benchmarks",
    +    message=(
    +        "⚡️It looks like you've configured a benchmarks_root in your config. "
    +        "Would you like to run the GitHub action in benchmark mode? "
    +        "This will show the impact of Codeflash's suggested optimizations on your benchmarks"
    +    ),
         default=True,
     )
    Suggestion importance[1-10]: 8

    __

    Why: The split string literal is currently passed as an unintended positional argument to inquirer_wrapper, causing a call signature error. Merging them into a single message argument correctly fixes the bug.

    Medium

    codeflash-ai bot added a commit that referenced this pull request May 15, 2025
    …127% in PR #208 (`bump-gha-uv-version`)
    
    Here's an optimized version of your program with reduced runtime, specifically targeting the bottlenecks.
    
    ### **Optimizations performed:**
    1. **Constant Folding:** The sys.version_info fetch and string formatting for the Python version is only necessary for the non-UV case and does not need to be constructed unless used.  
    2. **Precomputed Templates:** The output strings are constants and can be stored as such, to avoid reconstructing them on each call.  
    3. **Avoid Unnecessary Formatting:** For the setup-python path, the version string is constant across invocations of the same interpreter, so can use lazy-static initialization.
    4. **Reduced Function Overhead:** Restructured code to minimize code execution paths and avoid unnecessary work.
    
    ---
    
    
    
    ---
    
    **Summary of changes:**  
    - Only creates formatted strings once per interpreter lifetime, so on repeated heavy calls, time and allocations are minimized.
    - `sys.version_info` is not re-accessed on every call.
    - Preserved all logical comments; comments were added only where code was optimized.
    
    This rewrite should significantly improve per-call performance of `get_dependency_manager_installation_string()`.
    @codeflash-ai
    Copy link
    Contributor

    codeflash-ai bot commented May 15, 2025

    ⚡️ Codeflash found optimizations for this PR

    📄 127% (1.27x) speedup for get_dependency_manager_installation_string in codeflash/cli_cmds/cmd_init.py

    ⏱️ Runtime : 1.20 millisecond 529 microseconds (best of 182 runs)

    I created a new dependent PR with the suggested changes. Please review:

    If you approve, it will be merged into this PR (branch bump-gha-uv-version).

    codeflash-ai bot added a commit that referenced this pull request May 15, 2025
    …139% in PR #208 (`bump-gha-uv-version`)
    
    Here is an optimized version of your program. The main optimizations are.
    
    - Avoid repeated formatting of `python_version_string` by computing it once at module load time; the Python version doesn't change during runtime.
    - Move the string templates out of the function, so they are created just once.
    - Remove unnecessary usage of triple-quoted strings for templated outputs since there is no variable interpolation except one case.
    - Inline the conditional return for a slightly reduced call stack.
    - Use identity comparison `is` for Enum comparison (assuming `DependencyManager` is an `Enum`), which can be marginally faster.
    
    **Optimized Code:**
    
    
    
    **What changed and why:**
    
    - Pre-calculating the version string (and Python setup string) at module load time removes a significant amount of redundant per-call formatting (was hot in profiling!).
    - This also means `sys.version_info` is only accessed once.
    - Enum comparison is done with `is` which is the idiomatic and fastest way for Enums.
    - Templates are immediately ready, so nothing is constructed inside the function anymore; this yields maximum speedup for such a hot function; now it's a simple if/return.
    
    This completely eliminates *all* expensive operations from the hot path of `get_dependency_manager_installation_string`.
    @codeflash-ai
    Copy link
    Contributor

    codeflash-ai bot commented May 15, 2025

    ⚡️ Codeflash found optimizations for this PR

    📄 139% (1.39x) speedup for get_dependency_manager_installation_string in codeflash/cli_cmds/cmd_init.py

    ⏱️ Runtime : 2.85 milliseconds 1.19 millisecond (best of 114 runs)

    I created a new dependent PR with the suggested changes. Please review:

    If you approve, it will be merged into this PR (branch bump-gha-uv-version).

    @misrasaurabh1 misrasaurabh1 merged commit c848acd into main May 18, 2025
    13 of 15 checks passed
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    3 participants