mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
* WIP for integrate aider linter, see OpenDevin#2220
Updated aider linter to:
* Always return text and line numbers
* Moved extract line number more consistently
* Changed pylint to stop after first linter detects errors
Updated agentskills
* To get back a LintResult object and then use lines and text for error message and related line number
* Moved code for extracting line number to aider linter
Tests:
* Added additional unit tests for aider to test for
* Return values from lint failures
* Confirm linter works for non-configured languages like Ruby
* move to agent_skills, fixes not seeing skills error
* format/lint to new code, fix failing tests, remove unused code from aider linter
* small changes (remove litellm, fix readme typo)
* fix failing sandbox test
* keep, change dumping of metadata
* WIP for integrate aider linter, see OpenDevin#2220
Updated aider linter to:
* Always return text and line numbers
* Moved extract line number more consistently
* Changed pylint to stop after first linter detects errors
Updated agentskills
* To get back a LintResult object and then use lines and text for error message and related line number
* Moved code for extracting line number to aider linter
Tests:
* Added additional unit tests for aider to test for
* Return values from lint failures
* Confirm linter works for non-configured languages like Ruby
* move to agent_skills, fixes not seeing skills error
* format/lint to new code, fix failing tests, remove unused code from aider linter
* remove duplication of tree-sitter, grep-ast and update poetry.lock
* revert to main branch poetry.lock version
* only update necessary package
* fix jupyter kernel wrong interpreter issue (only for swebench)
* fix failing lint tests
* update syntax error checks for flake
* update poetry lock file
* update poetry.lock file, which update content-hash
* add grep ast
* remove extra stuff caused by merge
* update pyproject
* remove extra pytest fixture, ruff styling fixes
* lint files
* update poetry.lock file
---------
Co-authored-by: Jeff Katzy <jeffreyerickatz@gmail.com>
Co-authored-by: yufansong <yufan@risingwave-labs.com>
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
Co-authored-by: tobitege <tobitege@gmx.de>
This commit is contained in:
parent
b5e4cddce3
commit
fa6c12473e
@ -115,7 +115,7 @@ def process_instance(instance: Any, metadata: EvalMetadata, reset_logger: bool =
|
||||
'correct': correct,
|
||||
'answer_id': 'None',
|
||||
'model_id': metadata.model_name,
|
||||
'metadata': metadata.model_dump(),
|
||||
'metadata': metadata,
|
||||
'history': histories,
|
||||
'metrics': metrics,
|
||||
'error': state.last_error if state and state.last_error else None,
|
||||
|
||||
@ -21,7 +21,6 @@ import functools
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from inspect import signature
|
||||
from typing import Optional
|
||||
@ -32,10 +31,16 @@ from openai import OpenAI
|
||||
from pptx import Presentation
|
||||
from pylatexenc.latex2text import LatexNodes2Text
|
||||
|
||||
if __package__ is None or __package__ == '':
|
||||
from aider import Linter
|
||||
else:
|
||||
from .aider import Linter
|
||||
|
||||
CURRENT_FILE: str | None = None
|
||||
CURRENT_LINE = 1
|
||||
WINDOW = 100
|
||||
|
||||
|
||||
ENABLE_AUTO_LINT = os.getenv('ENABLE_AUTO_LINT', 'false').lower() == 'true'
|
||||
|
||||
# This is also used in unit tests!
|
||||
@ -124,53 +129,12 @@ def _lint_file(file_path: str) -> tuple[Optional[str], Optional[int]]:
|
||||
Returns:
|
||||
tuple[str, Optional[int]]: (lint_error, first_error_line_number)
|
||||
"""
|
||||
if file_path.endswith('.py'):
|
||||
# Define the flake8 command with selected error codes
|
||||
def _command_fn(executable):
|
||||
return [
|
||||
executable,
|
||||
'--isolated',
|
||||
'--select=F821,F822,F831,E112,E113,E999,E902',
|
||||
file_path,
|
||||
]
|
||||
|
||||
if os.path.exists('/opendevin/miniforge3/bin/flake8'):
|
||||
# when this function is called from the docker sandbox,
|
||||
# the flake8 command is available at /opendevin/miniforge3/bin/flake8
|
||||
executable = '/opendevin/miniforge3/bin/flake8'
|
||||
else:
|
||||
executable = 'flake8'
|
||||
|
||||
command = _command_fn(executable)
|
||||
result = subprocess.run(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
linter = Linter(root=os.getcwd())
|
||||
lint_error = linter.lint(file_path)
|
||||
if not lint_error:
|
||||
# Linting successful. No issues found.
|
||||
return None, None
|
||||
|
||||
# Extract the line number from the first error message
|
||||
error_message = result.stdout.decode().strip()
|
||||
lint_error = 'ERRORS:\n' + error_message
|
||||
first_error_line = None
|
||||
for line in error_message.splitlines(True):
|
||||
if line.strip():
|
||||
# The format of the error message is: <filename>:<line>:<column>: <error code> <error message>
|
||||
parts = line.split(':')
|
||||
if len(parts) >= 2:
|
||||
try:
|
||||
first_error_line = int(parts[1])
|
||||
break
|
||||
except ValueError:
|
||||
# Not a valid line number, continue to the next line
|
||||
continue
|
||||
|
||||
return lint_error, first_error_line
|
||||
|
||||
# Not a python file, skip linting
|
||||
return None, None
|
||||
return 'ERRORS:\n' + lint_error.text, lint_error.lines[0]
|
||||
|
||||
|
||||
def _print_window(file_path, targeted_line, window, return_str=False):
|
||||
|
||||
202
opendevin/runtime/plugins/agent_skills/aider/LICENSE.txt
Normal file
202
opendevin/runtime/plugins/agent_skills/aider/LICENSE.txt
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
8
opendevin/runtime/plugins/agent_skills/aider/README.md
Normal file
8
opendevin/runtime/plugins/agent_skills/aider/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Aider is AI pair programming in your terminal
|
||||
|
||||
Aider lets you pair program with LLMs,
|
||||
to edit code in your local git repository.
|
||||
|
||||
Please see the [original repository](https://github.com/paul-gauthier/aider) for more information.
|
||||
|
||||
OpenDevin has adapted and integrated its linter module ([original code](https://github.com/paul-gauthier/aider/blob/main/aider/linter.py)).
|
||||
6
opendevin/runtime/plugins/agent_skills/aider/__init__.py
Normal file
6
opendevin/runtime/plugins/agent_skills/aider/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
if __package__ is None or __package__ == '':
|
||||
from linter import Linter, LintResult
|
||||
else:
|
||||
from .linter import Linter, LintResult
|
||||
|
||||
__all__ = ['Linter', 'LintResult']
|
||||
223
opendevin/runtime/plugins/agent_skills/aider/linter.py
Normal file
223
opendevin/runtime/plugins/agent_skills/aider/linter.py
Normal file
@ -0,0 +1,223 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
import warnings
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from grep_ast import TreeContext, filename_to_lang
|
||||
from tree_sitter_languages import get_parser # noqa: E402
|
||||
|
||||
# tree_sitter is throwing a FutureWarning
|
||||
warnings.simplefilter('ignore', category=FutureWarning)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LintResult:
|
||||
text: str
|
||||
lines: list
|
||||
|
||||
|
||||
class Linter:
|
||||
def __init__(self, encoding='utf-8', root=None):
|
||||
self.encoding = encoding
|
||||
self.root = root
|
||||
|
||||
self.languages = dict(
|
||||
python=self.py_lint,
|
||||
)
|
||||
self.all_lint_cmd = None
|
||||
|
||||
def set_linter(self, lang, cmd):
|
||||
if lang:
|
||||
self.languages[lang] = cmd
|
||||
return
|
||||
|
||||
self.all_lint_cmd = cmd
|
||||
|
||||
def get_rel_fname(self, fname):
|
||||
if self.root:
|
||||
return os.path.relpath(fname, self.root)
|
||||
else:
|
||||
return fname
|
||||
|
||||
def run_cmd(self, cmd, rel_fname, code):
|
||||
cmd += ' ' + rel_fname
|
||||
cmd = cmd.split()
|
||||
|
||||
process = subprocess.Popen(
|
||||
cmd, cwd=self.root, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
stdout, _ = process.communicate()
|
||||
errors = stdout.decode().strip()
|
||||
self.returncode = process.returncode
|
||||
if self.returncode == 0:
|
||||
return # zero exit status
|
||||
|
||||
cmd = ' '.join(cmd)
|
||||
res = ''
|
||||
res += errors
|
||||
line_num = extract_error_line_from(res)
|
||||
return LintResult(text=res, lines=[line_num])
|
||||
|
||||
def get_abs_fname(self, fname):
|
||||
if os.path.isabs(fname):
|
||||
return fname
|
||||
elif os.path.isfile(fname):
|
||||
rel_fname = self.get_rel_fname(fname)
|
||||
return os.path.abspath(rel_fname)
|
||||
else: # if a temp file
|
||||
return self.get_rel_fname(fname)
|
||||
|
||||
def lint(self, fname, cmd=None) -> LintResult | None:
|
||||
code = Path(fname).read_text(self.encoding)
|
||||
absolute_fname = self.get_abs_fname(fname)
|
||||
if cmd:
|
||||
cmd = cmd.strip()
|
||||
if not cmd:
|
||||
lang = filename_to_lang(fname)
|
||||
if not lang:
|
||||
return None
|
||||
if self.all_lint_cmd:
|
||||
cmd = self.all_lint_cmd
|
||||
else:
|
||||
cmd = self.languages.get(lang)
|
||||
if callable(cmd):
|
||||
linkres = cmd(fname, absolute_fname, code)
|
||||
elif cmd:
|
||||
linkres = self.run_cmd(cmd, absolute_fname, code)
|
||||
else:
|
||||
linkres = basic_lint(absolute_fname, code)
|
||||
return linkres
|
||||
|
||||
def flake_lint(self, rel_fname, code):
|
||||
fatal = 'F821,F822,F831,E112,E113,E999,E902'
|
||||
flake8 = f'flake8 --select={fatal} --isolated'
|
||||
|
||||
try:
|
||||
flake_res = self.run_cmd(flake8, rel_fname, code)
|
||||
except FileNotFoundError:
|
||||
flake_res = None
|
||||
return flake_res
|
||||
|
||||
def py_lint(self, fname, rel_fname, code):
|
||||
error = self.flake_lint(rel_fname, code)
|
||||
if not error:
|
||||
error = lint_python_compile(fname, code)
|
||||
if not error:
|
||||
error = basic_lint(rel_fname, code)
|
||||
return error
|
||||
|
||||
|
||||
def lint_python_compile(fname, code):
|
||||
try:
|
||||
compile(code, fname, 'exec') # USE TRACEBACK BELOW HERE
|
||||
return
|
||||
except IndentationError as err:
|
||||
end_lineno = getattr(err, 'end_lineno', err.lineno)
|
||||
if isinstance(end_lineno, int):
|
||||
line_numbers = list(range(end_lineno - 1, end_lineno))
|
||||
else:
|
||||
line_numbers = []
|
||||
|
||||
tb_lines = traceback.format_exception(type(err), err, err.__traceback__)
|
||||
last_file_i = 0
|
||||
|
||||
target = '# USE TRACEBACK'
|
||||
target += ' BELOW HERE'
|
||||
for i in range(len(tb_lines)):
|
||||
if target in tb_lines[i]:
|
||||
last_file_i = i
|
||||
break
|
||||
tb_lines = tb_lines[:1] + tb_lines[last_file_i + 1 :]
|
||||
|
||||
res = ''.join(tb_lines)
|
||||
return LintResult(text=res, lines=line_numbers)
|
||||
|
||||
|
||||
def basic_lint(fname, code):
|
||||
"""
|
||||
Use tree-sitter to look for syntax errors, display them with tree context.
|
||||
"""
|
||||
|
||||
lang = filename_to_lang(fname)
|
||||
if not lang:
|
||||
return
|
||||
|
||||
parser = get_parser(lang)
|
||||
tree = parser.parse(bytes(code, 'utf-8'))
|
||||
|
||||
errors = traverse_tree(tree.root_node)
|
||||
if not errors:
|
||||
return
|
||||
return LintResult(text=f'{fname}:{errors[0]}', lines=errors)
|
||||
|
||||
|
||||
def extract_error_line_from(lint_error):
|
||||
# moved from opendevin.agentskills#_lint_file
|
||||
for line in lint_error.splitlines(True):
|
||||
if line.strip():
|
||||
# The format of the error message is: <filename>:<line>:<column>: <error code> <error message>
|
||||
parts = line.split(':')
|
||||
if len(parts) >= 2:
|
||||
try:
|
||||
first_error_line = int(parts[1])
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
return first_error_line
|
||||
|
||||
|
||||
def tree_context(fname, code, line_nums):
|
||||
context = TreeContext(
|
||||
fname,
|
||||
code,
|
||||
color=False,
|
||||
line_number=True,
|
||||
child_context=False,
|
||||
last_line=False,
|
||||
margin=0,
|
||||
mark_lois=True,
|
||||
loi_pad=3,
|
||||
# header_max=30,
|
||||
show_top_of_file_parent_scope=False,
|
||||
)
|
||||
line_nums = set(line_nums)
|
||||
context.add_lines_of_interest(line_nums)
|
||||
context.add_context()
|
||||
output = context.format()
|
||||
|
||||
return output
|
||||
|
||||
|
||||
# Traverse the tree to find errors
|
||||
def traverse_tree(node):
|
||||
errors = []
|
||||
if node.type == 'ERROR' or node.is_missing:
|
||||
line_no = node.start_point[0] + 1
|
||||
errors.append(line_no)
|
||||
|
||||
for child in node.children:
|
||||
errors += traverse_tree(child)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to parse files provided as command line arguments.
|
||||
"""
|
||||
if len(sys.argv) < 2:
|
||||
print('Usage: python linter.py <file1> <file2> ...')
|
||||
sys.exit(1)
|
||||
|
||||
linter = Linter(root=os.getcwd())
|
||||
for file_path in sys.argv[1:]:
|
||||
errors = linter.lint(file_path)
|
||||
if errors:
|
||||
print(errors)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -18,3 +18,4 @@ echo 'export PYTHONPATH=/opendevin/plugins/agent_skills:$PYTHONPATH' >> ~/.bashr
|
||||
source ~/.bashrc
|
||||
|
||||
$OPENDEVIN_PYTHON_INTERPRETER -m pip install flake8 python-docx PyPDF2 python-pptx pylatexenc openai opencv-python
|
||||
$OPENDEVIN_PYTHON_INTERPRETER -m pip install diskcache==5.6.3 grep-ast==0.3.2 tree-sitter==0.21.3 tree-sitter-languages==1.10.2
|
||||
|
||||
@ -18,6 +18,11 @@ source ~/.bashrc
|
||||
echo 'export PATH=$PATH:/opendevin/plugins/jupyter' >> ~/.bashrc
|
||||
export PATH=/opendevin/plugins/jupyter:$PATH
|
||||
|
||||
# Temporary add /opendevin/miniforge3/bin to PATH
|
||||
# will not persist after the end of the script
|
||||
# This fixes https://github.com/OpenDevin/OpenDevin/pull/2489#issuecomment-2223088169
|
||||
export PATH=/opendevin/miniforge3/bin:$PATH
|
||||
|
||||
# if user name is `opendevin`, add '/home/opendevin/.local/bin' to PATH
|
||||
if [ "$USER" = "opendevin" ]; then
|
||||
echo 'export PATH=$PATH:/home/opendevin/.local/bin' >> ~/.bashrc
|
||||
|
||||
135
poetry.lock
generated
135
poetry.lock
generated
@ -2479,6 +2479,21 @@ files = [
|
||||
docs = ["Sphinx"]
|
||||
test = ["objgraph", "psutil"]
|
||||
|
||||
[[package]]
|
||||
name = "grep-ast"
|
||||
version = "0.3.2"
|
||||
description = "A tool to grep through the AST of a source file"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "grep_ast-0.3.2-py3-none-any.whl", hash = "sha256:b7ceb84743983c3f4f5bca82f3374534cd9dbd759792d0dedf5648fedbb6f3fc"},
|
||||
{file = "grep_ast-0.3.2.tar.gz", hash = "sha256:d53bc7d25dfefafe77643fec189ab38e3cbd839d546c070a950ebedad82ee164"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pathspec = "*"
|
||||
tree-sitter-languages = ">=1.8.0"
|
||||
|
||||
[[package]]
|
||||
name = "grpc-google-iam-v1"
|
||||
version = "0.13.1"
|
||||
@ -4968,7 +4983,6 @@ description = "Nvidia JIT LTO Library"
|
||||
optional = false
|
||||
python-versions = ">=3"
|
||||
files = [
|
||||
{file = "nvidia_nvjitlink_cu12-12.5.40-py3-none-manylinux2014_aarch64.whl", hash = "sha256:004186d5ea6a57758fd6d57052a123c73a4815adf365eb8dd6a85c9eaa7535ff"},
|
||||
{file = "nvidia_nvjitlink_cu12-12.5.40-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d9714f27c1d0f0895cd8915c07a87a1d0029a0aa36acaf9156952ec2a8a12189"},
|
||||
{file = "nvidia_nvjitlink_cu12-12.5.40-py3-none-win_amd64.whl", hash = "sha256:c3401dc8543b52d3a8158007a0c1ab4e9c768fcbd24153a48c86972102197ddd"},
|
||||
]
|
||||
@ -5073,7 +5087,6 @@ optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f"},
|
||||
@ -8046,6 +8059,122 @@ torchhub = ["filelock", "huggingface-hub (>=0.23.2,<1.0)", "importlib-metadata",
|
||||
video = ["av (==9.2.0)", "decord (==0.6.0)"]
|
||||
vision = ["Pillow (>=10.0.1,<=15.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter"
|
||||
version = "0.21.3"
|
||||
description = "Python bindings for the Tree-Sitter parsing library"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tree-sitter-0.21.3.tar.gz", hash = "sha256:b5de3028921522365aa864d95b3c41926e0ba6a85ee5bd000e10dc49b0766988"},
|
||||
{file = "tree_sitter-0.21.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:351f302b6615230c9dac9829f0ba20a94362cd658206ca9a7b2d58d73373dfb0"},
|
||||
{file = "tree_sitter-0.21.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:766e79ae1e61271e7fdfecf35b6401ad9b47fc07a0965ad78e7f97fddfdf47a6"},
|
||||
{file = "tree_sitter-0.21.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c4d3d4d4b44857e87de55302af7f2d051c912c466ef20e8f18158e64df3542a"},
|
||||
{file = "tree_sitter-0.21.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84eedb06615461b9e2847be7c47b9c5f2195d7d66d31b33c0a227eff4e0a0199"},
|
||||
{file = "tree_sitter-0.21.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d33ea425df8c3d6436926fe2991429d59c335431bf4e3c71e77c17eb508be5a"},
|
||||
{file = "tree_sitter-0.21.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fae1ee0ff6d85e2fd5cd8ceb9fe4af4012220ee1e4cbe813305a316caf7a6f63"},
|
||||
{file = "tree_sitter-0.21.3-cp310-cp310-win_amd64.whl", hash = "sha256:bb41be86a987391f9970571aebe005ccd10222f39c25efd15826583c761a37e5"},
|
||||
{file = "tree_sitter-0.21.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54b22c3c2aab3e3639a4b255d9df8455da2921d050c4829b6a5663b057f10db5"},
|
||||
{file = "tree_sitter-0.21.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab6e88c1e2d5e84ff0f9e5cd83f21b8e5074ad292a2cf19df3ba31d94fbcecd4"},
|
||||
{file = "tree_sitter-0.21.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3fd34ed4cd5db445bc448361b5da46a2a781c648328dc5879d768f16a46771"},
|
||||
{file = "tree_sitter-0.21.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fabc7182f6083269ce3cfcad202fe01516aa80df64573b390af6cd853e8444a1"},
|
||||
{file = "tree_sitter-0.21.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f874c3f7d2a2faf5c91982dc7d88ff2a8f183a21fe475c29bee3009773b0558"},
|
||||
{file = "tree_sitter-0.21.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ee61ee3b7a4eedf9d8f1635c68ba4a6fa8c46929601fc48a907c6cfef0cfbcb2"},
|
||||
{file = "tree_sitter-0.21.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b7256c723642de1c05fbb776b27742204a2382e337af22f4d9e279d77df7aa2"},
|
||||
{file = "tree_sitter-0.21.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:669b3e5a52cb1e37d60c7b16cc2221c76520445bb4f12dd17fd7220217f5abf3"},
|
||||
{file = "tree_sitter-0.21.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2aa2a5099a9f667730ff26d57533cc893d766667f4d8a9877e76a9e74f48f0d3"},
|
||||
{file = "tree_sitter-0.21.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3e06ae2a517cf6f1abb682974f76fa760298e6d5a3ecf2cf140c70f898adf0"},
|
||||
{file = "tree_sitter-0.21.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af992dfe08b4fefcfcdb40548d0d26d5d2e0a0f2d833487372f3728cd0772b48"},
|
||||
{file = "tree_sitter-0.21.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c7cbab1dd9765138505c4a55e2aa857575bac4f1f8a8b0457744a4fefa1288e6"},
|
||||
{file = "tree_sitter-0.21.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1e66aeb457d1529370fcb0997ae5584c6879e0e662f1b11b2f295ea57e22f54"},
|
||||
{file = "tree_sitter-0.21.3-cp312-cp312-win_amd64.whl", hash = "sha256:013c750252dc3bd0e069d82e9658de35ed50eecf31c6586d0de7f942546824c5"},
|
||||
{file = "tree_sitter-0.21.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4986a8cb4acebd168474ec2e5db440e59c7888819b3449a43ce8b17ed0331b07"},
|
||||
{file = "tree_sitter-0.21.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6e217fee2e7be7dbce4496caa3d1c466977d7e81277b677f954d3c90e3272ec2"},
|
||||
{file = "tree_sitter-0.21.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32a88afff4f2bc0f20632b0a2aa35fa9ae7d518f083409eca253518e0950929"},
|
||||
{file = "tree_sitter-0.21.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3652ac9e47cdddf213c5d5d6854194469097e62f7181c0a9aa8435449a163a9"},
|
||||
{file = "tree_sitter-0.21.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:60b4df3298ff467bc01e2c0f6c2fb43aca088038202304bf8e41edd9fa348f45"},
|
||||
{file = "tree_sitter-0.21.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:00e4d0c99dff595398ef5e88a1b1ddd53adb13233fb677c1fd8e497fb2361629"},
|
||||
{file = "tree_sitter-0.21.3-cp38-cp38-win_amd64.whl", hash = "sha256:50c91353a26946e4dd6779837ecaf8aa123aafa2d3209f261ab5280daf0962f5"},
|
||||
{file = "tree_sitter-0.21.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b17b8648b296ccc21a88d72ca054b809ee82d4b14483e419474e7216240ea278"},
|
||||
{file = "tree_sitter-0.21.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f2f057fd01d3a95cbce6794c6e9f6db3d376cb3bb14e5b0528d77f0ec21d6478"},
|
||||
{file = "tree_sitter-0.21.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:839759de30230ffd60687edbb119b31521d5ac016749358e5285816798bb804a"},
|
||||
{file = "tree_sitter-0.21.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df40aa29cb7e323898194246df7a03b9676955a0ac1f6bce06bc4903a70b5f7"},
|
||||
{file = "tree_sitter-0.21.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1d9be27dde007b569fa78ff9af5fe40d2532c998add9997a9729e348bb78fa59"},
|
||||
{file = "tree_sitter-0.21.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c4ac87735e6f98fe085244c7c020f0177d13d4c117db72ba041faa980d25d69d"},
|
||||
{file = "tree_sitter-0.21.3-cp39-cp39-win_amd64.whl", hash = "sha256:fbbd137f7d9a5309fb4cb82e2c3250ba101b0dd08a8abdce815661e6cf2cbc19"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-languages"
|
||||
version = "1.10.2"
|
||||
description = "Binary Python wheels for all tree sitter languages."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5580348f0b20233b1d5431fa178ccd3d07423ca4a3275df02a44608fd72344b9"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:103c7466644486b1e9e03850df46fc6aa12f13ca636c74f173270276220ac80b"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d13db84511c6f1a7dc40383b66deafa74dabd8b877e3d65ab253f3719eccafd6"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57adfa32be7e465b54aa72f915f6c78a2b66b227df4f656b5d4fbd1ca7a92b3f"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6385e033e460ceb8f33f3f940335f422ef2b763700a04f0089391a68b56153"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dfa3f38cc5381c5aba01dd7494f59b8a9050e82ff6e06e1233e3a0cbae297e3c"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9f195155acf47f8bc5de7cee46ecd07b2f5697f007ba89435b51ef4c0b953ea5"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2de330e2ac6d7426ca025a3ec0f10d5640c3682c1d0c7702e812dcfb44b58120"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-win32.whl", hash = "sha256:c9731cf745f135d9770eeba9bb4e2ff4dabc107b5ae9b8211e919f6b9100ea6d"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:6dd75851c41d0c3c4987a9b7692d90fa8848706c23115669d8224ffd6571e357"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7eb7d7542b2091c875fe52719209631fca36f8c10fa66970d2c576ae6a1b8289"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b41bcb00974b1c8a1800c7f1bb476a1d15a0463e760ee24872f2d53b08ee424"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f370cd7845c6c81df05680d5bd96db8a99d32b56f4728c5d05978911130a853"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1dc195c88ef4c72607e112a809a69190e096a2e5ebc6201548b3e05fdd169ad"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae34ac314a7170be24998a0f994c1ac80761d8d4bd126af27ee53a023d3b849"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:01b5742d5f5bd675489486b582bd482215880b26dde042c067f8265a6e925d9c"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ab1cbc46244d34fd16f21edaa20231b2a57f09f092a06ee3d469f3117e6eb954"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0b1149e7467a4e92b8a70e6005fe762f880f493cf811fc003554b29f04f5e7c8"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-win32.whl", hash = "sha256:049276343962f4696390ee555acc2c1a65873270c66a6cbe5cb0bca83bcdf3c6"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:7f3fdd468a577f04db3b63454d939e26e360229b53c80361920aa1ebf2cd7491"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c0f4c8b2734c45859edc7fcaaeaab97a074114111b5ba51ab4ec7ed52104763c"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eecd3c1244ac3425b7a82ba9125b4ddb45d953bbe61de114c0334fd89b7fe782"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15db3c8510bc39a80147ee7421bf4782c15c09581c1dc2237ea89cefbd95b846"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92c6487a6feea683154d3e06e6db68c30e0ae749a7ce4ce90b9e4e46b78c85c7"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2f1cd1d1bdd65332f9c2b67d49dcf148cf1ded752851d159ac3e5ee4f4d260"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:976c8039165b8e12f17a01ddee9f4e23ec6e352b165ad29b44d2bf04e2fbe77e"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:dafbbdf16bf668a580902e1620f4baa1913e79438abcce721a50647564c687b9"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1aeabd3d60d6d276b73cd8f3739d595b1299d123cc079a317f1a5b3c5461e2ca"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-win32.whl", hash = "sha256:fab8ee641914098e8933b87ea3d657bea4dd00723c1ee7038b847b12eeeef4f5"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:5e606430d736367e5787fa5a7a0c5a1ec9b85eded0b3596bbc0d83532a40810b"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:838d5b48a7ed7a17658721952c77fda4570d2a069f933502653b17e15a9c39c9"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b3c71b1d278c2889e018ee77b8ee05c384e2e3334dec798f8b611c4ab2d1e"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faa00abcb2c819027df58472da055d22fa7dfcb77c77413d8500c32ebe24d38b"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e102fbbf02322d9201a86a814e79a9734ac80679fdb9682144479044f401a73"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0b87cf1a7b03174ba18dfd81582be82bfed26803aebfe222bd20e444aba003"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c0f1b9af9cb67f0b942b020da9fdd000aad5e92f2383ae0ba7a330b318d31912"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5a4076c921f7a4d31e643843de7dfe040b65b63a238a5aa8d31d93aabe6572aa"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-win32.whl", hash = "sha256:fa6391a3a5d83d32db80815161237b67d70576f090ce5f38339206e917a6f8bd"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:55649d3f254585a064121513627cf9788c1cfdadbc5f097f33d5ba750685a4c0"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6f85d1edaa2d22d80d4ea5b6d12b95cf3644017b6c227d0d42854439e02e8893"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d78feed4a764ef3141cb54bf00fe94d514d8b6e26e09423e23b4c616fcb7938c"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1aca27531f9dd5308637d76643372856f0f65d0d28677d1bcf4211e8ed1ad0"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1031ea440dafb72237437d754eff8940153a3b051e3d18932ac25e75ce060a15"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99d3249beaef2c9fe558ecc9a97853c260433a849dcc68266d9770d196c2e102"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:59a4450f262a55148fb7e68681522f0c2a2f6b7d89666312a2b32708d8f416e1"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ce74eab0e430370d5e15a96b6c6205f93405c177a8b2e71e1526643b2fb9bab1"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9b4dd2b6b3d24c85dffe33d6c343448869eaf4f41c19ddba662eb5d65d8808f4"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-win32.whl", hash = "sha256:92d734fb968fe3927a7596d9f0459f81a8fa7b07e16569476b28e27d0d753348"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a13f7d38f2eeb75f7cf127d1201346093748c270d686131f0cbc50e42870a1"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f8c6a936ae99fdd8857e91f86c11c2f5e507ff30631d141d98132bb7ab2c8638"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c283a61423f49cdfa7b5a5dfbb39221e3bd126fca33479cd80749d4d7a6b7349"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e60be6bdcff923386a54a5edcb6ff33fc38ab0118636a762024fa2bc98de55"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c00069f9575bd831eabcce2cdfab158dde1ed151e7e5614c2d985ff7d78a7de1"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475ff53203d8a43ccb19bb322fa2fb200d764001cc037793f1fadd714bb343da"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26fe7c9c412e4141dea87ea4b3592fd12e385465b5bdab106b0d5125754d4f60"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8fed27319957458340f24fe14daad467cd45021da034eef583519f83113a8c5e"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3657a491a7f96cc75a3568ddd062d25f3be82b6a942c68801a7b226ff7130181"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-win32.whl", hash = "sha256:33f7d584d01a7a3c893072f34cfc64ec031f3cfe57eebc32da2f8ac046e101a7"},
|
||||
{file = "tree_sitter_languages-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:1b944af3ee729fa70fc8ae82224a9ff597cdb63addea084e0ea2fa2b0ec39bb7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tree-sitter = "*"
|
||||
|
||||
[[package]]
|
||||
name = "triton"
|
||||
version = "2.2.0"
|
||||
@ -8979,4 +9108,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "fe79a236957f5e04e353e437144d75f90788172f7448f6c388e19bf374bad2b0"
|
||||
content-hash = "b3ff28ec1edbc942a34c21b4aab3c8c5eb8af7449d6d5885d888acd7b4d9fca4"
|
||||
|
||||
@ -37,6 +37,8 @@ tenacity = "^8.5.0"
|
||||
zope-interface = "6.4.post2"
|
||||
pathspec = "^0.12.1"
|
||||
google-cloud-aiplatform = "*"
|
||||
grep-ast = "0.3.2"
|
||||
tree-sitter = "0.21.3"
|
||||
|
||||
[tool.poetry.group.llama-index.dependencies]
|
||||
llama-index = "*"
|
||||
@ -57,13 +59,19 @@ pytest = "*"
|
||||
pytest-cov = "*"
|
||||
pytest-asyncio = "*"
|
||||
pytest-forked = "*"
|
||||
flake8 = "*"
|
||||
openai = "*"
|
||||
python-docx = "*"
|
||||
PyPDF2 = "*"
|
||||
pylatexenc = "*"
|
||||
python-pptx = "*"
|
||||
opencv-python = "*"
|
||||
pandas = "*"
|
||||
reportlab = "*"
|
||||
|
||||
[tool.coverage.run]
|
||||
concurrency = ["gevent"]
|
||||
|
||||
|
||||
[tool.poetry.group.runtime.dependencies]
|
||||
jupyterlab = "*"
|
||||
notebook = "*"
|
||||
@ -97,7 +105,6 @@ ignore = ["D1"]
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
|
||||
[tool.poetry.group.evaluation.dependencies]
|
||||
streamlit = "*"
|
||||
whatthepatch = "*"
|
||||
|
||||
@ -46,6 +46,12 @@ def _generate_test_file_with_lines(temp_path, num_lines) -> str:
|
||||
return file_path
|
||||
|
||||
|
||||
def _generate_ruby_test_file_with_lines(temp_path, num_lines) -> str:
|
||||
file_path = temp_path / 'test_file.rb'
|
||||
file_path.write_text('\n' * num_lines)
|
||||
return file_path
|
||||
|
||||
|
||||
def _calculate_window_bounds(current_line, total_lines, window_size):
|
||||
"""Calculate the bounds of the window around the current line."""
|
||||
half_window = window_size // 2
|
||||
@ -58,6 +64,12 @@ def _calculate_window_bounds(current_line, total_lines, window_size):
|
||||
return start, end
|
||||
|
||||
|
||||
def _generate_ruby_test_file_with_lines(temp_path, num_lines) -> str:
|
||||
file_path = temp_path / 'test_file.rb'
|
||||
file_path.write_text('\n' * num_lines)
|
||||
return file_path
|
||||
|
||||
|
||||
def test_open_file_unexist_path():
|
||||
with pytest.raises(FileNotFoundError):
|
||||
open_file('/unexist/path/a.txt')
|
||||
@ -1490,3 +1502,46 @@ def test_parse_pptx(tmp_path):
|
||||
'Hello, this is the second test PPTX slide.\n\n'
|
||||
)
|
||||
assert output == expected_output, f'Expected output does not match. Got: {output}'
|
||||
|
||||
|
||||
def test_lint_file_fail_non_python(tmp_path, monkeypatch, capsys):
|
||||
monkeypatch.setattr(
|
||||
'opendevin.runtime.plugins.agent_skills.agentskills.ENABLE_AUTO_LINT', True
|
||||
)
|
||||
|
||||
current_line = 1
|
||||
|
||||
file_path = _generate_ruby_test_file_with_lines(tmp_path, 1)
|
||||
|
||||
open_file(str(file_path), current_line)
|
||||
insert_content_at_line(
|
||||
str(file_path), 1, "def print_hello_world()\n puts 'Hello World'"
|
||||
)
|
||||
result = capsys.readouterr().out
|
||||
assert result is not None
|
||||
expected = (
|
||||
f'[File: {file_path} (1 lines total)]\n'
|
||||
'(this is the beginning of the file)\n'
|
||||
'1|\n'
|
||||
'(this is the end of the file)\n'
|
||||
'[Your proposed edit has introduced new syntax error(s). Please understand the errors and retry your edit command.]\n'
|
||||
'ERRORS:\n'
|
||||
f'{file_path}:1\n'
|
||||
'[This is how your edit would have looked if applied]\n'
|
||||
'-------------------------------------------------\n'
|
||||
'(this is the beginning of the file)\n'
|
||||
'1|def print_hello_world()\n'
|
||||
"2| puts 'Hello World'\n"
|
||||
'(this is the end of the file)\n'
|
||||
'-------------------------------------------------\n\n'
|
||||
'[This is the original code before your edit]\n'
|
||||
'-------------------------------------------------\n'
|
||||
'(this is the beginning of the file)\n'
|
||||
'1|\n'
|
||||
'(this is the end of the file)\n'
|
||||
'-------------------------------------------------\n'
|
||||
'Your changes have NOT been applied. Please fix your edit command and try again.\n'
|
||||
'You either need to 1) Specify the correct start/end line arguments or 2) Correct your edit code.\n'
|
||||
'DO NOT re-run the same failed edit command. Running it again will lead to the same error.\n'
|
||||
)
|
||||
assert result.split('\n') == expected.split('\n')
|
||||
|
||||
197
tests/unit/test_aider_linter.py
Normal file
197
tests/unit/test_aider_linter.py
Normal file
@ -0,0 +1,197 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from opendevin.runtime.plugins.agent_skills.aider import Linter, LintResult
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_file(tmp_path):
|
||||
# Fixture to create a temporary file
|
||||
temp_name = os.path.join(tmp_path, 'lint-test.py')
|
||||
with open(temp_name, 'w', encoding='utf-8') as tmp_file:
|
||||
tmp_file.write("""def foo():
|
||||
print("Hello, World!")
|
||||
foo()
|
||||
""")
|
||||
tmp_file.close()
|
||||
yield temp_name
|
||||
os.remove(temp_name)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_ruby_file_errors(tmp_path):
|
||||
# Fixture to create a temporary file
|
||||
temp_name = os.path.join(tmp_path, 'lint-test.rb')
|
||||
with open(temp_name, 'w', encoding='utf-8') as tmp_file:
|
||||
tmp_file.write("""def foo():
|
||||
print("Hello, World!")
|
||||
foo()
|
||||
""")
|
||||
tmp_file.close()
|
||||
yield temp_name
|
||||
os.remove(temp_name)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_ruby_file_errors_parentheses(tmp_path):
|
||||
# Fixture to create a temporary file
|
||||
temp_name = os.path.join(tmp_path, 'lint-test.rb')
|
||||
with open(temp_name, 'w', encoding='utf-8') as tmp_file:
|
||||
tmp_file.write("""def print_hello_world()\n puts 'Hello World'\n""")
|
||||
tmp_file.close()
|
||||
yield temp_name
|
||||
os.remove(temp_name)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_ruby_file_correct(tmp_path):
|
||||
# Fixture to create a temporary file
|
||||
temp_name = os.path.join(tmp_path, 'lint-test.rb')
|
||||
with open(temp_name, 'w', encoding='utf-8') as tmp_file:
|
||||
tmp_file.write("""def foo
|
||||
puts "Hello, World!"
|
||||
end
|
||||
foo
|
||||
""")
|
||||
tmp_file.close()
|
||||
yield temp_name
|
||||
os.remove(temp_name)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def linter(tmp_path):
|
||||
return Linter(root=tmp_path)
|
||||
|
||||
|
||||
def test_get_rel_fname(linter, temp_file, tmp_path):
|
||||
# Test get_rel_fname method
|
||||
rel_fname = linter.get_rel_fname(temp_file)
|
||||
|
||||
assert rel_fname == os.path.relpath(temp_file, tmp_path)
|
||||
|
||||
|
||||
def test_run_cmd(linter, temp_file):
|
||||
# Test run_cmd method with a simple command
|
||||
result = linter.run_cmd('echo', temp_file, '')
|
||||
|
||||
assert result is None # echo command should return zero exit status
|
||||
|
||||
|
||||
def test_set_linter(linter):
|
||||
# Test set_linter method
|
||||
def custom_linter(fname, rel_fname, code):
|
||||
return LintResult(text='Custom Linter', lines=[1])
|
||||
|
||||
linter.set_linter('custom', custom_linter)
|
||||
|
||||
assert 'custom' in linter.languages
|
||||
assert linter.languages['custom'] == custom_linter
|
||||
|
||||
|
||||
def test_py_lint(linter, temp_file):
|
||||
# Test py_lint method
|
||||
result = linter.py_lint(
|
||||
temp_file, linter.get_rel_fname(temp_file), "print('Hello, World!')\n"
|
||||
)
|
||||
|
||||
assert result is None # No lint errors expected for this simple code
|
||||
|
||||
|
||||
def test_py_lint_fail(linter, temp_file):
|
||||
# Test py_lint method
|
||||
result = linter.py_lint(
|
||||
temp_file, linter.get_rel_fname(temp_file), "print('Hello, World!')\n"
|
||||
)
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_basic_lint(temp_file):
|
||||
from opendevin.runtime.plugins.agent_skills.aider.linter import basic_lint
|
||||
|
||||
poorly_formatted_code = """
|
||||
def foo()
|
||||
print("Hello, World!")
|
||||
print("Wrong indent")
|
||||
foo(
|
||||
"""
|
||||
result = basic_lint(temp_file, poorly_formatted_code)
|
||||
|
||||
assert isinstance(result, LintResult)
|
||||
assert result.text == f'{temp_file}:2'
|
||||
assert 2 in result.lines
|
||||
|
||||
|
||||
def test_basic_lint_fail_returns_text_and_lines(temp_file):
|
||||
from opendevin.runtime.plugins.agent_skills.aider.linter import basic_lint
|
||||
|
||||
poorly_formatted_code = """
|
||||
def foo()
|
||||
print("Hello, World!")
|
||||
print("Wrong indent")
|
||||
foo(
|
||||
"""
|
||||
|
||||
result = basic_lint(temp_file, poorly_formatted_code)
|
||||
|
||||
assert isinstance(result, LintResult)
|
||||
assert result.text == f'{temp_file}:2'
|
||||
assert 2 in result.lines
|
||||
|
||||
|
||||
def test_lint_python_compile(temp_file):
|
||||
from opendevin.runtime.plugins.agent_skills.aider.linter import lint_python_compile
|
||||
|
||||
result = lint_python_compile(temp_file, "print('Hello, World!')\n")
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_lint_python_compile_fail_returns_text_and_lines(temp_file):
|
||||
from opendevin.runtime.plugins.agent_skills.aider.linter import lint_python_compile
|
||||
|
||||
poorly_formatted_code = """
|
||||
def foo()
|
||||
print("Hello, World!")
|
||||
print("Wrong indent")
|
||||
foo(
|
||||
"""
|
||||
result = lint_python_compile(temp_file, poorly_formatted_code)
|
||||
|
||||
assert temp_file in result.text
|
||||
assert 1 in result.lines
|
||||
|
||||
|
||||
def test_lint(linter, temp_file):
|
||||
result = linter.lint(temp_file)
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_lint_fail(linter, temp_file):
|
||||
# Test lint method
|
||||
with open(temp_file, 'w', encoding='utf-8') as lint_file:
|
||||
lint_file.write("""
|
||||
def foo()
|
||||
print("Hello, World!")
|
||||
print("Wrong indent")
|
||||
foo(
|
||||
""")
|
||||
errors = linter.lint(temp_file)
|
||||
|
||||
assert errors is not None
|
||||
|
||||
|
||||
def test_lint_pass_ruby(linter, temp_ruby_file_correct):
|
||||
result = linter.lint(temp_ruby_file_correct)
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_lint_fail_ruby(linter, temp_ruby_file_errors):
|
||||
errors = linter.lint(temp_ruby_file_errors)
|
||||
assert errors is not None
|
||||
|
||||
|
||||
def test_lint_fail_ruby_no_parentheses(linter, temp_ruby_file_errors_parentheses):
|
||||
errors = linter.lint(temp_ruby_file_errors_parentheses)
|
||||
assert errors is not None
|
||||
@ -259,7 +259,7 @@ def _test_sandbox_jupyter_agentskills_fileop_pwd_impl(box, config: AppConfig):
|
||||
"""
|
||||
[Your proposed edit has introduced new syntax error(s). Please understand the errors and retry your edit command.]
|
||||
ERRORS:
|
||||
hello.py:1:3: E999 IndentationError: unexpected indent
|
||||
/workspace/test/hello.py:1:3: E999 IndentationError: unexpected indent
|
||||
[This is how your edit would have looked if applied]
|
||||
-------------------------------------------------
|
||||
(this is the beginning of the file)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user