Skip to content
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

Commenter and Explainer #2

Merged
merged 9 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 83 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,88 @@
# CoderGPT
# CoderGPT CLI

A language agnostic code optimizer.
## Description

CoderGPT CLI is a command line interface for CoderGPT, a state-of-the-art code generation tool. It allows developers to interact with the CoderGPT functionalities directly from the terminal, streamlining their workflow and enhancing productivity.

## Author

Harshad Hegde

## Installation

To use the CoderGPT CLI, clone the repository and install the required dependencies.

```shell
pip install codergpt
```

## Usage

Run the CLI using the following syntax:

```shell
code [OPTIONS] COMMAND [ARGS]...
```

### Options

- `-v, --verbose INTEGER`: Set verbosity level (0, 1, or 2).
- `-q, --quiet`: Enable quiet mode.
- `--version`: Display version information.

### Commands

#### inspect

Inspect a package and display a file-language map.

```shell
code inspect <path>
```

#### explain

Explain a specific function or class within a package.

```shell
code explain <path> [--function <function_name>] [--classname <class_name>]
```

#### comment

Add comments to the code in a package.

```shell
code comment <path> [--overwrite/--no-overwrite]
```

## Development

The CLI is built using Python and the `click` library. Below is an example of how to define a new command:

```python
import click
from codergpt import CoderGPT

coder = CoderGPT()

@click.command()
@click.argument('path', type=click.Path(exists=True))
def new_command(path):
# Command logic here
pass
```

## Contributing

Contributions are welcome! Please read our contributing guidelines before submitting pull requests.

## License

This project is licensed under the MIT License - see the LICENSE.md file for details.

# Acknowledgements

This [cookiecutter](https://cookiecutter.readthedocs.io/en/stable/README.html) project was developed from the [monarch-project-template](https://github.com/monarch-initiative/monarch-project-template) template and will be kept up-to-date using [cruft](https://cruft.github.io/cruft/).

For more information on CoderGPT CLI, please visit [the official documentation]().
8 changes: 8 additions & 0 deletions src/codergpt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

import importlib_metadata

from codergpt.commenter import CodeCommenter
from codergpt.explainer import CodeExplainer

from .main import CoderGPT

try:
__version__ = importlib_metadata.version(__name__)
except importlib_metadata.PackageNotFoundError:
# package is not installed
__version__ = "0.0.0" # pragma: no cover


__all__ = ["CoderGPT", "CodeExplainer", "CodeCommenter"]
60 changes: 57 additions & 3 deletions src/codergpt/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
"""Command line interface for CoderGPT."""
"""
Command line interface for CoderGPT.

Author: Harshad Hegde

This module provides a command line interface (CLI) for CoderGPT, a powerful code generation tool.

Usage:
python codergpt_cli.py [OPTIONS] COMMAND [ARGS]...

Options:
-v, --verbose INTEGER Verbosity level (0, 1 or 2).
-q, --quiet Run in quiet mode.
--version Show the version and exit.

Commands:
inspect Inspect package to show file-language-map.
explain Inspect package to show file-language-map.
comment Inspect package to show file-language-map.

"""

import logging
from pathlib import Path
Expand All @@ -16,6 +36,9 @@
logger = logging.getLogger(__name__)

path_argument = click.argument("path", type=click.Path(exists=True))
overwrite_option = click.option(
"--overwrite/--no-overwrite", is_flag=True, default=False, help="Overwrite the existing file."
)

coder = CoderGPT()

Expand Down Expand Up @@ -44,7 +67,11 @@ def main(verbose: int, quiet: bool):
@main.command()
@path_argument
def inspect(path: Union[str, Path, TextIO]):
"""Inspect package to show file-language-map."""
"""
Inspect package to show file-language-map.

:param path: Path to the package.
"""
coder.inspect_package(path=path)


Expand All @@ -53,7 +80,13 @@ def inspect(path: Union[str, Path, TextIO]):
@click.option("-f", "--function", help="Function name to explain.")
@click.option("-c", "--classname", help="Class name to explain.")
def explain(path: Union[str, Path], function: str, classname: str):
"""Inspect package to show file-language-map."""
"""
Inspect package to show file-language-map.

:param path: Path to the package.
:param function: Name of the function to explain.
:param classname: Name of the class to explain.
"""
# Ensure path is a string or Path object for consistency
if isinstance(path, str):
path = Path(path)
Expand All @@ -65,5 +98,26 @@ def explain(path: Union[str, Path], function: str, classname: str):
raise ValueError("The path provided is not a file.")


@main.command("comment")
@path_argument
@overwrite_option
def add_comments(path: Union[str, Path], overwrite: bool = False):
"""
Inspect package to show file-language-map.

:param path: Path to the package.
:param overwrite: Flag to indicate whether to overwrite existing files.
"""
# Ensure path is a string or Path object for consistency
if isinstance(path, str):
path = Path(path)

# Check if path is a file
if path.is_file():
coder.commenter(path=path, overwrite=overwrite)
else:
raise ValueError("The path provided is not a file.")


if __name__ == "__main__":
main()
4 changes: 4 additions & 0 deletions src/codergpt/commenter/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
"""Commenter module for the package."""

from .commenter import CodeCommenter

__all__ = ["CodeCommenter"]
46 changes: 46 additions & 0 deletions src/codergpt/commenter/commenter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Commenter Module."""

import os
from typing import Any, Dict

from langchain_core.runnables.base import RunnableSerializable


class CodeCommenter:
"""Code Explainer class that extracts and explains code from a given file."""

def __init__(self, chain: RunnableSerializable[Dict, Any]):
"""
Initialize the CodeExplainer class with a runnable chain.

:param chain: A RunnableSerializable object capable of executing tasks.
"""
self.chain = chain

def comment(self, code: str, filename: str, overwrite: bool = False):
"""
Comment the contents of the code string by invoking the runnable chain and write it to a new file.

:param code: The string containing the code to be commented.
:param filename: The original filename of the code file.
:param overwrite: A boolean indicating whether to overwrite the original file. Default is False.
"""
response = self.chain.invoke(
{
"input": f"Rewrite and return this code with\
comments and docstrings in :param: format: \n{code}\n"
}
)

# Extract the commented code from the response if necessary
commented_code = response.content

new_filename = filename
if not overwrite:
# Create a new filename with the _updated suffix
base, ext = os.path.splitext(filename)
new_filename = f"{base}_updated{ext}"

# Write the commented code to the new file
with open(new_filename, "w") as updated_file:
updated_file.write(commented_code)
2 changes: 1 addition & 1 deletion src/codergpt/explainer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

from .explainer import CodeExplainer

__all__ = ["CodeExplainer", "ExpressionEvaluator"]
__all__ = ["CodeExplainer"]
38 changes: 2 additions & 36 deletions src/codergpt/explainer/explainer.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
"""Explainer Module."""

import ast
from pathlib import Path
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, Optional

from langchain_core.runnables.base import RunnableSerializable

from codergpt.utils.expression_evaluator import ExpressionEvaluator


class CodeExplainer:
"""Code Explainer class that extracts and explains code from a given file."""
Expand All @@ -20,32 +16,7 @@ def __init__(self, chain: RunnableSerializable[Dict, Any]):
"""
self.chain = chain

def get_function_code(
self, filename: str, function_name: Optional[str] = None, class_name: Optional[str] = None
) -> Optional[str]:
"""
Extract and return the source code of the specified function or class from a file.

:param filename: The path to the file containing the code.
:param function_name: The name of the function to extract code for. Default is None.
:param class_name: The name of the class to extract code for. Default is None.
:return: The extracted source code of the specified function or class, if found.
"""
with open(filename, "r") as source_file:
source_code = source_file.read()

# Parse the source code into an AST
parsed_code = ast.parse(source_code)

# Create a visitor instance and walk through the AST
visitor = ExpressionEvaluator(source_code=source_code, function_name=function_name, class_name=class_name)
visitor.visit(parsed_code)
if function_name:
return visitor.function_code
elif class_name:
return visitor.class_code

def explain(self, path: Union[str, Path], function: Optional[str] = None, classname: Optional[str] = None):
def explain(self, code: str, function: Optional[str] = None, classname: Optional[str] = None):
"""
Explain the contents of the code file by invoking the runnable chain.

Expand All @@ -54,20 +25,15 @@ def explain(self, path: Union[str, Path], function: Optional[str] = None, classn
:param classname: The name of the class to explain. Default is None.
"""
if function:
code = self.get_function_code(filename=path, function_name=function)
response = self.chain.invoke({"input": f"Explain the following code: \n\n```\n{code}\n```"})

# Pretty print the response
print(f"Explanation for '{function}':\n{response.content}")
elif classname:
code = self.get_function_code(filename=path, class_name=classname)
response = self.chain.invoke({"input": f"Explain the following code: \n\n```\n{code}\n```"})
# Pretty print the response
print(f"Explanation for '{classname}':\n{response.content}")
else:
# Explain full code
with open(path, "r") as file:
code = file.read()
response = self.chain.invoke({"input": f"Explain the following code: \n\n```\n{code}\n```"})
# Pretty print the response
print(f"Explanation for the code:\n{response.content}")
Loading
Loading