Skip to content

Commit

Permalink
Merge pull request #2 from hrshdhgd/commenter
Browse files Browse the repository at this point in the history
Commenter and Explainer
  • Loading branch information
hrshdhgd authored Feb 9, 2024
2 parents 0ddb440 + d0b041b commit d1cf1cc
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 58 deletions.
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

0 comments on commit d1cf1cc

Please sign in to comment.