code-analyser 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_analyser-0.1.0.dist-info/METADATA +283 -0
- code_analyser-0.1.0.dist-info/RECORD +34 -0
- code_analyser-0.1.0.dist-info/WHEEL +4 -0
- code_analyser-0.1.0.dist-info/licenses/LICENSE +21 -0
- codelens/__init__.py +7 -0
- codelens/__main__.py +19 -0
- codelens/analyzers/__init__.py +30 -0
- codelens/analyzers/base.py +139 -0
- codelens/analyzers/manager.py +207 -0
- codelens/analyzers/python_analyzer.py +344 -0
- codelens/analyzers/similarity_analyzer.py +512 -0
- codelens/api/__init__.py +1 -0
- codelens/api/routes/__init__.py +1 -0
- codelens/api/routes/analysis.py +441 -0
- codelens/api/routes/reports.py +438 -0
- codelens/api/routes/rubrics.py +349 -0
- codelens/api/schemas.py +305 -0
- codelens/cli.py +297 -0
- codelens/core/__init__.py +1 -0
- codelens/core/config.py +91 -0
- codelens/db/__init__.py +1 -0
- codelens/db/database.py +57 -0
- codelens/main.py +111 -0
- codelens/models/__init__.py +14 -0
- codelens/models/assignments.py +105 -0
- codelens/models/reports.py +172 -0
- codelens/models/rubrics.py +76 -0
- codelens/services/__init__.py +37 -0
- codelens/services/batch_processor.py +508 -0
- codelens/services/code_executor.py +310 -0
- codelens/services/sandbox.py +375 -0
- codelens/services/similarity_service.py +449 -0
- codelens/utils/__init__.py +29 -0
- codelens/utils/helpers.py +217 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-analyser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Automated Code Analysis & Grading Assistant for Educators
|
|
5
|
+
Project-URL: Homepage, https://github.com/yourusername/codelens
|
|
6
|
+
Project-URL: Repository, https://github.com/yourusername/codelens.git
|
|
7
|
+
Author: CodeLens Team
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Education
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: aiosqlite>=0.19.0
|
|
17
|
+
Requires-Dist: alembic>=1.13.0
|
|
18
|
+
Requires-Dist: docker>=6.1.0
|
|
19
|
+
Requires-Dist: fastapi>=0.104.0
|
|
20
|
+
Requires-Dist: httpx>=0.25.0
|
|
21
|
+
Requires-Dist: mypy>=1.7.0
|
|
22
|
+
Requires-Dist: passlib[bcrypt]>=1.7.4
|
|
23
|
+
Requires-Dist: pydantic-settings>=2.1.0
|
|
24
|
+
Requires-Dist: pydantic>=2.5.0
|
|
25
|
+
Requires-Dist: python-jose[cryptography]>=3.3.0
|
|
26
|
+
Requires-Dist: python-multipart>=0.0.6
|
|
27
|
+
Requires-Dist: ruff>=0.1.0
|
|
28
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
29
|
+
Requires-Dist: structlog>=23.2.0
|
|
30
|
+
Requires-Dist: uvicorn[standard]>=0.24.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: black>=23.11.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: isort>=5.12.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# CodeLens
|
|
41
|
+
|
|
42
|
+
<!-- BADGES:START -->
|
|
43
|
+
[](https://github.com/topics/edtech) [](https://github.com/topics/automated-grading) [](https://github.com/topics/code-analysis) [](https://github.com/topics/docker) [](https://github.com/topics/education) [](https://github.com/topics/fastapi) [](https://github.com/topics/microservice) [](https://github.com/topics/plagiarism-detection) [](https://github.com/topics/python) [](https://github.com/topics/static-analysis)
|
|
44
|
+
<!-- BADGES:END -->
|
|
45
|
+
|
|
46
|
+
**Automated Code Analysis & Grading Assistant for Educators**
|
|
47
|
+
|
|
48
|
+
CodeLens is a comprehensive microservice designed to help educators analyze, validate, and grade student code submissions across multiple programming languages. It provides automated analysis, plagiarism detection, sandboxed code execution, and detailed feedback generation.
|
|
49
|
+
|
|
50
|
+
## 🚀 Features
|
|
51
|
+
|
|
52
|
+
### Core Analysis
|
|
53
|
+
- **Static Code Analysis**: Syntax validation, style checking, complexity metrics
|
|
54
|
+
- **Configurable Tools**: Support for ruff, mypy, and other analysis tools
|
|
55
|
+
- **Multi-language Support**: Currently supports Python with extensible architecture
|
|
56
|
+
- **Quality Metrics**: Lines of code, cyclomatic complexity, maintainability index
|
|
57
|
+
|
|
58
|
+
### Code Execution
|
|
59
|
+
- **Secure Sandboxing**: Docker-based isolated execution environment
|
|
60
|
+
- **Test Execution**: Support for pytest and unittest frameworks
|
|
61
|
+
- **Resource Limits**: CPU, memory, and time constraints for safety
|
|
62
|
+
- **Input/Output Validation**: Compare expected vs actual outputs
|
|
63
|
+
|
|
64
|
+
### Plagiarism Detection
|
|
65
|
+
- **Multiple Methods**: AST structural, token-based, line-based similarity
|
|
66
|
+
- **Cross-submission Comparison**: Compare against other student submissions
|
|
67
|
+
- **Configurable Thresholds**: Adjustable similarity detection sensitivity
|
|
68
|
+
- **Review System**: Manual review and flagging of potential plagiarism
|
|
69
|
+
|
|
70
|
+
### Batch Processing
|
|
71
|
+
- **Directory Processing**: Analyze entire folders of submissions
|
|
72
|
+
- **Parallel Processing**: Concurrent analysis for improved performance
|
|
73
|
+
- **Student Info Extraction**: Automatic extraction of student IDs from filenames
|
|
74
|
+
- **CLI Interface**: Command-line tools for instructors
|
|
75
|
+
|
|
76
|
+
### Grading & Feedback
|
|
77
|
+
- **Rubric-based Grading**: Configurable grading criteria and weights
|
|
78
|
+
- **Automated Scoring**: Calculate grades based on multiple factors
|
|
79
|
+
- **Detailed Feedback**: Constructive comments and improvement suggestions
|
|
80
|
+
- **Progress Tracking**: Track student performance over time
|
|
81
|
+
|
|
82
|
+
## 🏗️ Architecture
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
codelens/
|
|
86
|
+
├── api/ # FastAPI routes and schemas
|
|
87
|
+
├── analyzers/ # Code analysis engines
|
|
88
|
+
├── services/ # Business logic services
|
|
89
|
+
├── models/ # Database models
|
|
90
|
+
├── db/ # Database configuration
|
|
91
|
+
├── core/ # Configuration and settings
|
|
92
|
+
└── utils/ # Utility functions
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 🛠️ Installation
|
|
96
|
+
|
|
97
|
+
1. **Clone the repository**
|
|
98
|
+
```bash
|
|
99
|
+
git clone <repository-url>
|
|
100
|
+
cd code-lens
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
2. **Install dependencies**
|
|
104
|
+
```bash
|
|
105
|
+
pip install -e .
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
3. **Install analysis tools**
|
|
109
|
+
```bash
|
|
110
|
+
pip install ruff mypy pytest
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
4. **Setup Docker** (for code execution)
|
|
114
|
+
```bash
|
|
115
|
+
docker pull python:3.11-slim
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
5. **Initialize database**
|
|
119
|
+
```bash
|
|
120
|
+
python -m codelens.main
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 🚀 Quick Start
|
|
124
|
+
|
|
125
|
+
### Web API Server
|
|
126
|
+
|
|
127
|
+
Start the FastAPI server:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Development mode
|
|
131
|
+
uvicorn codelens.main:app --reload
|
|
132
|
+
|
|
133
|
+
# Production mode
|
|
134
|
+
uvicorn codelens.main:app --host 0.0.0.0 --port 8000
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The API will be available at `http://localhost:8003` with documentation at `/docs`.
|
|
138
|
+
|
|
139
|
+
### CLI Usage
|
|
140
|
+
|
|
141
|
+
Analyze a single file:
|
|
142
|
+
```bash
|
|
143
|
+
python -m codelens analyze submission.py --student-id cs123456
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Process a directory of submissions:
|
|
147
|
+
```bash
|
|
148
|
+
python -m codelens batch /path/to/submissions --language python --detailed
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Generate batch report:
|
|
152
|
+
```bash
|
|
153
|
+
python -m codelens batch /submissions --output results.json --rubric-id 1
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 📡 API Examples
|
|
157
|
+
|
|
158
|
+
### Analyze Python Code
|
|
159
|
+
```bash
|
|
160
|
+
curl -X POST "http://localhost:8003/api/v1/analyze/python" \
|
|
161
|
+
-H "Content-Type: application/json" \
|
|
162
|
+
-d '{
|
|
163
|
+
"code": "def hello():\n print(\"Hello, World!\")",
|
|
164
|
+
"language": "python",
|
|
165
|
+
"student_id": "cs123456",
|
|
166
|
+
"check_similarity": true,
|
|
167
|
+
"run_tests": false
|
|
168
|
+
}'
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Batch Analysis
|
|
172
|
+
```bash
|
|
173
|
+
curl -X POST "http://localhost:8003/api/v1/analyze/batch" \
|
|
174
|
+
-H "Content-Type: application/json" \
|
|
175
|
+
-d '{
|
|
176
|
+
"files": [
|
|
177
|
+
{"code": "def add(a, b): return a + b", "path": "student1.py"},
|
|
178
|
+
{"code": "def multiply(x, y): return x * y", "path": "student2.py"}
|
|
179
|
+
],
|
|
180
|
+
"language": "python",
|
|
181
|
+
"check_similarity": true
|
|
182
|
+
}'
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Create Rubric
|
|
186
|
+
```bash
|
|
187
|
+
curl -X POST "http://localhost:8003/api/v1/rubrics/" \
|
|
188
|
+
-H "Content-Type: application/json" \
|
|
189
|
+
-d '{
|
|
190
|
+
"name": "Python Assignment 1",
|
|
191
|
+
"language": "python",
|
|
192
|
+
"criteria": {"functionality": 40, "style": 30, "documentation": 30},
|
|
193
|
+
"weights": {"functionality": 0.4, "style": 0.3, "documentation": 0.3},
|
|
194
|
+
"total_points": 100
|
|
195
|
+
}'
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## ⚙️ Configuration
|
|
199
|
+
|
|
200
|
+
Configuration is managed through environment variables and the `codelens/core/config.py` file:
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
# Analysis tools
|
|
204
|
+
RUFF_ENABLED=true
|
|
205
|
+
MYPY_ENABLED=true
|
|
206
|
+
MAX_LINE_LENGTH=88
|
|
207
|
+
|
|
208
|
+
# Execution limits
|
|
209
|
+
EXECUTION_TIMEOUT=30
|
|
210
|
+
MEMORY_LIMIT=128m
|
|
211
|
+
CPU_LIMIT=0.5
|
|
212
|
+
|
|
213
|
+
# Similarity detection
|
|
214
|
+
SIMILARITY_ENABLED=true
|
|
215
|
+
SIMILARITY_THRESHOLD=0.8
|
|
216
|
+
|
|
217
|
+
# Database
|
|
218
|
+
DATABASE_URL=sqlite+aiosqlite:///./codelens.db
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 🔒 Security
|
|
222
|
+
|
|
223
|
+
- **Sandboxed Execution**: All code runs in isolated Docker containers
|
|
224
|
+
- **Resource Limits**: CPU, memory, and time constraints prevent abuse
|
|
225
|
+
- **No Network Access**: Containers run without network connectivity
|
|
226
|
+
- **Input Validation**: All inputs are validated and sanitized
|
|
227
|
+
- **Code Analysis Only**: Original submissions are not stored permanently
|
|
228
|
+
|
|
229
|
+
## 📊 Supported Analysis
|
|
230
|
+
|
|
231
|
+
### Python
|
|
232
|
+
- **Linting**: ruff (configurable rules)
|
|
233
|
+
- **Type Checking**: mypy (optional)
|
|
234
|
+
- **Metrics**: Complexity, maintainability, documentation coverage
|
|
235
|
+
- **Testing**: pytest, unittest support
|
|
236
|
+
- **Similarity**: AST-based structural comparison
|
|
237
|
+
|
|
238
|
+
### Future Languages
|
|
239
|
+
The architecture supports extension to JavaScript, HTML/CSS, Java, and other languages.
|
|
240
|
+
|
|
241
|
+
## 🤝 Educational Use Cases
|
|
242
|
+
|
|
243
|
+
- **Introductory Programming Courses**: Automated grading for basic assignments
|
|
244
|
+
- **Code Quality Assessment**: Teaching best practices and style guidelines
|
|
245
|
+
- **Plagiarism Detection**: Identifying potential academic dishonesty
|
|
246
|
+
- **Progress Tracking**: Monitoring student improvement over time
|
|
247
|
+
- **Immediate Feedback**: Helping students learn from mistakes
|
|
248
|
+
|
|
249
|
+
## 📈 Example Workflow
|
|
250
|
+
|
|
251
|
+
1. **Instructor**: Creates assignment with rubric
|
|
252
|
+
2. **Students**: Submit code files (via LMS or direct upload)
|
|
253
|
+
3. **CodeLens**: Analyzes submissions automatically
|
|
254
|
+
4. **System**: Generates grades and detailed feedback
|
|
255
|
+
5. **Instructor**: Reviews flagged similarities and edge cases
|
|
256
|
+
6. **Students**: Receive feedback and suggestions for improvement
|
|
257
|
+
|
|
258
|
+
## 🧪 Testing
|
|
259
|
+
|
|
260
|
+
Run the test suite:
|
|
261
|
+
```bash
|
|
262
|
+
pytest tests/
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Run with coverage:
|
|
266
|
+
```bash
|
|
267
|
+
pytest --cov=codelens tests/
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## 📝 License
|
|
271
|
+
|
|
272
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
273
|
+
|
|
274
|
+
## 🙏 Acknowledgments
|
|
275
|
+
|
|
276
|
+
- Built with FastAPI for high-performance web APIs
|
|
277
|
+
- Uses Docker for secure code execution
|
|
278
|
+
- Powered by ruff and mypy for Python analysis
|
|
279
|
+
- Inspired by the need for fair and consistent code grading
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
**CodeLens**: Empowering educators with intelligent code analysis 🔍✨
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
codelens/__init__.py,sha256=sx1HI1jOpPOML0ACWaCe1Hp1Kdu2K6lswGk9zRDhoV8,207
|
|
2
|
+
codelens/__main__.py,sha256=4_KQpQu_5Z0VSvejCIvAGjnMEVgo8fMpaWuRYHAWVYE,431
|
|
3
|
+
codelens/cli.py,sha256=w5haGGK7xaril4zcxdCIzks54GDcxRBn5SwEbVqd8Bg,11799
|
|
4
|
+
codelens/main.py,sha256=rdjDKtbCMWZmq87KkznFxrmt1LVcHs8lTbbMc1ZC6kw,2999
|
|
5
|
+
codelens/analyzers/__init__.py,sha256=rbtiHE0yEmuf0OxHTBoxfeuGVNXWNiCV5IlBYYcvRbg,749
|
|
6
|
+
codelens/analyzers/base.py,sha256=f0dglJCP-bYKhQjJlh5nL8qM5S_X0Xl8L8ir1oQnWAM,4259
|
|
7
|
+
codelens/analyzers/manager.py,sha256=UaNO7o7UZh1AtT9gKBSuqqtSBWBInDSwzJAJB5NDmGs,7043
|
|
8
|
+
codelens/analyzers/python_analyzer.py,sha256=SyES-FlJKvkFxryztcQUkVM7DPT90O9ff2nREZFCRFk,13368
|
|
9
|
+
codelens/analyzers/similarity_analyzer.py,sha256=sp-sFpheTeRrARS-S7wGj5EX9pWEzm8E37poZDJ-9I8,18864
|
|
10
|
+
codelens/api/__init__.py,sha256=Mow0Ffn6nVs3QDEXSEZHOMWwKmSBMeSxduYjJt9CjkA,31
|
|
11
|
+
codelens/api/schemas.py,sha256=Dd9xNP5T5p4eiJpBe3byLO0yBRLJnOF32jDCZaDqsL4,10939
|
|
12
|
+
codelens/api/routes/__init__.py,sha256=JXHLVi48XM5_zVjoBSyHvUWqT4XDhkHP9X_cjMLbk8c,24
|
|
13
|
+
codelens/api/routes/analysis.py,sha256=NEmYUoRHth_-wOnHKddzjT6-6EdaKPSjvuG0oglPUIc,17035
|
|
14
|
+
codelens/api/routes/reports.py,sha256=Vd-7S3PfeXXlB-X0MPtZEEK6FnlGb9Y_0LqS9U2JsPE,14539
|
|
15
|
+
codelens/api/routes/rubrics.py,sha256=rhFE9c547xhLk93Ip-XaypU4PZcuOkYcBsuXgWHvNLg,11562
|
|
16
|
+
codelens/core/__init__.py,sha256=Xls_Bpc6QdAjMS_pbplPEzHjHuAtjtwaK0Dld0iTD-A,34
|
|
17
|
+
codelens/core/config.py,sha256=-NNVDeimo3yPhLHU9M8pOiAIb8Lw0R2ECWETMq0R7Jg,2348
|
|
18
|
+
codelens/db/__init__.py,sha256=20e81aCej9FuAojE6v5ATpfrueelBUXRXlvFIY6IYo0,44
|
|
19
|
+
codelens/db/database.py,sha256=9f4zQDdemrIJgNgzhiUC-7lnsck4kq34SkDQr58-fU8,1440
|
|
20
|
+
codelens/models/__init__.py,sha256=QtfavOkLD6nvBF8ggCG2KBSvwujDmlDAWXJ9vM7L7sw,298
|
|
21
|
+
codelens/models/assignments.py,sha256=2OzS45_C9jrDgtfeSvvLbOa71wfQ76SBBNM560ivNjY,3999
|
|
22
|
+
codelens/models/reports.py,sha256=cIff31nGdmB4kJy-udEsLRRFHEFZ_EcyX0ZOFW3O8dI,6266
|
|
23
|
+
codelens/models/rubrics.py,sha256=y4HrPyYb5AA7Dnvf2FV7q6a5DQHL3QKluJzqAj7xVuw,2826
|
|
24
|
+
codelens/services/__init__.py,sha256=W5iKEV_YDcKrznlOLeqRj0CAiT5HdBhhjQoHrv8l4kc,859
|
|
25
|
+
codelens/services/batch_processor.py,sha256=TcSUkacWg1Qz1J7W3w0f31kyggSucMzl6KUxDxngPyc,17739
|
|
26
|
+
codelens/services/code_executor.py,sha256=bC2pqColCTeB7jcUbPTK5_mjah0TWbwXoLjY0EFCJvU,10862
|
|
27
|
+
codelens/services/sandbox.py,sha256=Oho5E43M1nYYUHh70wKuF9dLlMwFqTvcmI0Kp29Qj98,14059
|
|
28
|
+
codelens/services/similarity_service.py,sha256=vZu9OSCf6e1efp3NUmouYjCT1I3Rl-tMysI_iL11ekM,16549
|
|
29
|
+
codelens/utils/__init__.py,sha256=-rjYOnL-A4W9vUf00lclhb09GGYxLJRrnveveBdyCok,714
|
|
30
|
+
codelens/utils/helpers.py,sha256=xguLk0S9RFm1TcyskFYsYzeIFfTq6Km8I2EQZFyKtXY,5961
|
|
31
|
+
code_analyser-0.1.0.dist-info/METADATA,sha256=TFP0d0nnJTNwM5A0QMfp9mXlTutNRm42SHyNlqWHPO4,9661
|
|
32
|
+
code_analyser-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
33
|
+
code_analyser-0.1.0.dist-info/licenses/LICENSE,sha256=tP28nznO05HKBQN8uZ9NSvmcpzCWMj4P8e6F7IDj24Q,1070
|
|
34
|
+
code_analyser-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Borck
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
codelens/__init__.py
ADDED
codelens/__main__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entry point for running CodeLens as a module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from codelens.cli import main
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
try:
|
|
12
|
+
exit_code = asyncio.run(main())
|
|
13
|
+
sys.exit(exit_code)
|
|
14
|
+
except KeyboardInterrupt:
|
|
15
|
+
print("\nInterrupted by user", file=sys.stderr)
|
|
16
|
+
sys.exit(1)
|
|
17
|
+
except Exception as e:
|
|
18
|
+
print(f"Unexpected error: {str(e)}", file=sys.stderr)
|
|
19
|
+
sys.exit(1)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Code analyzers for different languages"""
|
|
2
|
+
|
|
3
|
+
from .base import AnalysisIssue, AnalysisResult, BaseAnalyzer, CodeMetrics, Severity
|
|
4
|
+
from .manager import AnalyzerManager, analyzer_manager
|
|
5
|
+
from .python_analyzer import PythonAnalyzer
|
|
6
|
+
from .similarity_analyzer import (
|
|
7
|
+
PythonSimilarityAnalyzer,
|
|
8
|
+
SimilarityDetector,
|
|
9
|
+
SimilarityMatch,
|
|
10
|
+
SimilarityMethod,
|
|
11
|
+
SimilarityResult,
|
|
12
|
+
similarity_detector,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"BaseAnalyzer",
|
|
17
|
+
"AnalysisResult",
|
|
18
|
+
"AnalysisIssue",
|
|
19
|
+
"CodeMetrics",
|
|
20
|
+
"Severity",
|
|
21
|
+
"PythonAnalyzer",
|
|
22
|
+
"AnalyzerManager",
|
|
23
|
+
"analyzer_manager",
|
|
24
|
+
"SimilarityMethod",
|
|
25
|
+
"SimilarityMatch",
|
|
26
|
+
"SimilarityResult",
|
|
27
|
+
"PythonSimilarityAnalyzer",
|
|
28
|
+
"SimilarityDetector",
|
|
29
|
+
"similarity_detector",
|
|
30
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base analyzer interface and common functionality
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import hashlib
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Severity(Enum):
|
|
14
|
+
"""Issue severity levels"""
|
|
15
|
+
INFO = "info"
|
|
16
|
+
WARNING = "warning"
|
|
17
|
+
ERROR = "error"
|
|
18
|
+
CRITICAL = "critical"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class AnalysisIssue:
|
|
23
|
+
"""Represents an issue found during analysis"""
|
|
24
|
+
line: int
|
|
25
|
+
column: int = 0
|
|
26
|
+
severity: Severity = Severity.WARNING
|
|
27
|
+
code: str = "" # Error/warning code (e.g., "E501", "W292")
|
|
28
|
+
message: str = ""
|
|
29
|
+
category: str = "general" # style, syntax, logic, complexity, etc.
|
|
30
|
+
suggestion: str | None = None # How to fix the issue
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class CodeMetrics:
|
|
35
|
+
"""Code quality metrics"""
|
|
36
|
+
lines_of_code: int = 0
|
|
37
|
+
lines_of_comments: int = 0
|
|
38
|
+
blank_lines: int = 0
|
|
39
|
+
cyclomatic_complexity: int = 0
|
|
40
|
+
cognitive_complexity: int = 0
|
|
41
|
+
function_count: int = 0
|
|
42
|
+
class_count: int = 0
|
|
43
|
+
max_nesting_depth: int = 0
|
|
44
|
+
maintainability_index: float = 0.0
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class AnalysisResult:
|
|
49
|
+
"""Results from code analysis"""
|
|
50
|
+
success: bool
|
|
51
|
+
issues: list[AnalysisIssue]
|
|
52
|
+
metrics: CodeMetrics
|
|
53
|
+
execution_time: float = 0.0
|
|
54
|
+
analyzer_version: str = ""
|
|
55
|
+
raw_output: str | None = None # Raw analyzer output for debugging
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class BaseAnalyzer(ABC):
|
|
59
|
+
"""Base class for all code analyzers"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, config: dict[str, Any] | None = None):
|
|
62
|
+
self.config = config or {}
|
|
63
|
+
self.name = self.__class__.__name__
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
async def analyze(self, code: str, file_path: str = "temp.py") -> AnalysisResult:
|
|
67
|
+
"""
|
|
68
|
+
Analyze the given code and return results
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
code: Source code to analyze
|
|
72
|
+
file_path: Optional file path for context
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
AnalysisResult with issues and metrics
|
|
76
|
+
"""
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def get_version(self) -> str:
|
|
81
|
+
"""Get the version of the analyzer tool"""
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
def get_code_hash(self, code: str) -> str:
|
|
85
|
+
"""Generate SHA-256 hash of code for caching/identification"""
|
|
86
|
+
return hashlib.sha256(code.encode('utf-8')).hexdigest()
|
|
87
|
+
|
|
88
|
+
def parse_ast(self, code: str) -> ast.AST | None:
|
|
89
|
+
"""Parse code into AST, return None if syntax error"""
|
|
90
|
+
try:
|
|
91
|
+
return ast.parse(code)
|
|
92
|
+
except SyntaxError:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
def calculate_basic_metrics(self, code: str) -> CodeMetrics:
|
|
96
|
+
"""Calculate basic code metrics from source"""
|
|
97
|
+
lines = code.split('\n')
|
|
98
|
+
|
|
99
|
+
metrics = CodeMetrics()
|
|
100
|
+
metrics.lines_of_code = len([line for line in lines if line.strip()])
|
|
101
|
+
metrics.blank_lines = len([line for line in lines if not line.strip()])
|
|
102
|
+
metrics.lines_of_comments = len([
|
|
103
|
+
line for line in lines
|
|
104
|
+
if line.strip().startswith('#') and not line.strip().startswith('#!')
|
|
105
|
+
])
|
|
106
|
+
|
|
107
|
+
# Try to get AST metrics
|
|
108
|
+
try:
|
|
109
|
+
tree = ast.parse(code)
|
|
110
|
+
metrics.function_count = len([
|
|
111
|
+
node for node in ast.walk(tree)
|
|
112
|
+
if isinstance(node, ast.FunctionDef)
|
|
113
|
+
])
|
|
114
|
+
metrics.class_count = len([
|
|
115
|
+
node for node in ast.walk(tree)
|
|
116
|
+
if isinstance(node, ast.ClassDef)
|
|
117
|
+
])
|
|
118
|
+
metrics.max_nesting_depth = self._calculate_nesting_depth(tree)
|
|
119
|
+
except SyntaxError:
|
|
120
|
+
pass # Keep default values if code doesn't parse
|
|
121
|
+
|
|
122
|
+
return metrics
|
|
123
|
+
|
|
124
|
+
def _calculate_nesting_depth(self, node: ast.AST, current_depth: int = 0) -> int:
|
|
125
|
+
"""Calculate maximum nesting depth in AST"""
|
|
126
|
+
max_depth = current_depth
|
|
127
|
+
|
|
128
|
+
# Nodes that increase nesting depth
|
|
129
|
+
nesting_nodes = (ast.If, ast.While, ast.For, ast.With, ast.Try)
|
|
130
|
+
|
|
131
|
+
for child in ast.iter_child_nodes(node):
|
|
132
|
+
if isinstance(child, nesting_nodes):
|
|
133
|
+
child_depth = self._calculate_nesting_depth(child, current_depth + 1)
|
|
134
|
+
max_depth = max(max_depth, child_depth)
|
|
135
|
+
else:
|
|
136
|
+
child_depth = self._calculate_nesting_depth(child, current_depth)
|
|
137
|
+
max_depth = max(max_depth, child_depth)
|
|
138
|
+
|
|
139
|
+
return max_depth
|