Skip to content

Commit

Permalink
First Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lolgab committed Oct 24, 2019
0 parents commit 83fdbc0
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 0 deletions.
56 changes: 56 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
version: 2.1

orbs:
codacy: codacy/[email protected]
codacy_plugins_test: codacy/[email protected]

jobs:
test_patterns:
machine:
image: ubuntu-1604:201903-01
working_directory: ~/workdir
steps:
- attach_workspace:
at: ~/
- run:
command: |
docker build -t $CIRCLE_PROJECT_REPONAME:latest .
docker run $CIRCLE_PROJECT_REPONAME:latest codacy_prospector_test.py
docker save --output ~/workdir/docker-image.tar $CIRCLE_PROJECT_REPONAME:latest
- persist_to_workspace:
paths:
- workdir/*
root: ~/
workflows:
version: 2
compile_test_deploy:
jobs:
- codacy/checkout_and_version
- test_patterns:
requires:
- codacy/checkout_and_version
- codacy_plugins_test/run:
name: plugins_test
requires:
- test_patterns
- codacy/shell:
name: publish_dockerhub
context: CodacyDocker
cmd: |
docker load --input ~/workdir/docker-image.tar
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker tag $CIRCLE_PROJECT_REPONAME codacy/$CIRCLE_PROJECT_REPONAME:$(cat .version)
docker tag $CIRCLE_PROJECT_REPONAME codacy/$CIRCLE_PROJECT_REPONAME:latest
docker push codacy/$CIRCLE_PROJECT_REPONAME:$(cat .version)
docker push codacy/$CIRCLE_PROJECT_REPONAME:latest
requires:
- plugins_test
filters:
branches:
only:
- master
- codacy/tag_version:
name: tag_version
context: CodacyAWS
requires:
- publish_dockerhub
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* @lolgab @ljmf00 @andreaTP @rtfpessoa @bmbferreira @DReigada @pedrocodacy

*.yml @h314to @paulopontesm

12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/RUNNING_PID
/logs/
/project/*-shim.sbt
/project/project
target
.idea
.DS_Store
.metals
.bloop
.vscode
__pycache__
.mypy_cache
1 change: 1 addition & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version = "2.0.1"
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.7-slim
COPY requirements.txt requirements.txt
RUN pip3 install --no-cache-dir -r requirements.txt
COPY src/codacy_prospector.py codacy_prospector.py
COPY src/codacy_prospector_test.py codacy_prospector_test.py
COPY docs /docs
RUN useradd -u 2004 -U docker
RUN mkdir /home/docker
RUN chown -R docker:docker /docs /home/docker
USER docker
ENTRYPOINT [ "python3.7" ]
CMD [ "codacy_prospector.py" ]
13 changes: 13 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright 2015 Codacy.

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.
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[![Codacy Badge](https://api.codacy.com/project/badge/grade/8bd24fe19ffb4c3ea0e947225e962d28)](https://www.codacy.com/app/Codacy/codacy-prospector)
[![Build Status](https://circleci.com/gh/codacy/codacy-pylint.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/codacy/codacy-prospector)

# Codacy Prospector

This is the docker engine we use at Codacy to have [Prospector](https://github.com/PyCQA/prospector) support.
You can also create a docker to integrate the tool and language of your choice!
See the [codacy-engine-scala-seed](https://github.com/codacy/codacy-engine-scala-seed) repository for more information.

## Usage

You can create the docker by doing:

```bash
docker build -t codacy-prospector:latest .
```

The docker is ran with the following command:

```bash
docker run -it -v $srcDir:/src codacy-prospector:latest
```

## Generate Docs

1. Update the version in `docs/patterns.json`
2. Run the DocGenerator:

```bash
sbt "doc-generator/run"
```

## Test

We use the [codacy-plugins-test](https://github.com/codacy/codacy-plugins-test) to test our external tools integration.
You can follow the instructions there to make sure your tool is working as expected.

## What is Codacy?

[Codacy](https://www.codacy.com/) is an Automated Code Review Tool that monitors your technical debt, helps you improve your code quality, teaches best practices to your developers, and helps you save time in Code Reviews.

### Among Codacy’s features

- Identify new Static Analysis issues
- Commit and Pull Request Analysis with GitHub, BitBucket/Stash, GitLab (and also direct git repositories)
- Auto-comments on Commits and Pull Requests
- Integrations with Slack, HipChat, Jira, YouTrack
- Track issues in Code Style, Security, Error Proneness, Performance, Unused Code and other categories

Codacy also helps keep track of Code Coverage, Code Duplication, and Code Complexity.

Codacy supports PHP, Python, Ruby, Java, JavaScript, and Scala, among others.

### Free for Open Source

Codacy is free for Open Source projects.
1 change: 1 addition & 0 deletions docs/description/description.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
11 changes: 11 additions & 0 deletions docs/patterns.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "Prospector",
"version": "1.1.7",
"patterns": [
{
"patternId": "pylint_unused-variable",
"level": "Error",
"category": "CodeStyle"
}
]
}
8 changes: 8 additions & 0 deletions docs/tests/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
##Patterns: pylint_unused-variable
import json
def readJsonFile(path):
##Err: pylint_unused-variable
a = 1
with open(path, 'r') as file:
res = json.loads(file.read())
return res
3 changes: 3 additions & 0 deletions docs/tool-description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Prospector is a tool to analyse Python code and output information about errors, potential problems, convention violations and complexity.

It brings together the functionality of other Python analysis tools such as [Pylint](https://pylint.readthedocs.io/), [pep8](https://pep8.readthedocs.io/), and [McCabe complexity](https://pypi.python.org/pypi/mccabe).
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mypy==0.720
prospector[with_everything]==1.1.7
jsonpickle==1.2
Django==2.2.5
Flask==1.1.1
140 changes: 140 additions & 0 deletions src/codacy_prospector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import os
import sys
import json
import jsonpickle
from subprocess import Popen, PIPE
import ast
import glob
import re
import signal
from contextlib import contextmanager

@contextmanager
def timeout(time):
# Register a function to raise a TimeoutError on the signal.
signal.signal(signal.SIGALRM, lambda: sys.exit(2))
# Schedule the signal to be sent after ``time``.
signal.alarm(time)
yield

DEFAULT_TIMEOUT = 16 * 60
def getTimeout(timeoutString):
l = timeoutString.split()
if len(l) != 2 or not l[0].isdigit():
return DEFAULT_TIMEOUT
elif l[1] == "second" or l[1] == "seconds":
return int(l[0])
elif l[1] == "minute" or l[1] == "minutes":
return int(l[0]) * 60
elif l[1] == "hour" or l[1] == "hours":
return int(l[0]) * 60 * 60
else:
return DEFAULT_TIMEOUT

class Result:
def __init__(self, filename, message, patternId, line):
self.filename = filename
self.message = message
self.patternId = patternId
self.line = line
def __str__(self):
return f'Result({self.filename},{self.message},{self.patternId},{self.line})'
def __repr__(self):
return self.__str__()
def __eq__(self, o):
return self.filename == o.filename and self.message == o.message and self.patternId == o.patternId and self.line == o.line

def toJson(obj): return jsonpickle.encode(obj, unpicklable=False)

def readJsonFile(path):
with open(path, 'r') as file:
res = json.loads(file.read())
return res

def runProspector(options, files, cwd=None):
process = Popen(
['python3', '-m', 'prospector', ] + options + files,
stdout=PIPE,
cwd=cwd
)
stdout = process.communicate()[0]
return stdout.decode('utf-8')

def isPython3(f):
try:
with open(f, 'r') as stream:
try:
ast.parse(stream.read())
except (ValueError, TypeError, UnicodeError):
# Assume it's the current interpreter.
return True
except SyntaxError:
# the other version or an actual syntax error on current interpreter
return False
else:
return True
except Exception:
# Shouldn't happen, but if it does, just assume there's
# something inherently wrong with the file.
return True

def parseResult(json_text):
messages = json.loads(json_text)["messages"]
def createResults():
for res in messages:
location = res['location']
yield Result(filename=location['path'], message=res['message'], patternId=f"{res['source']}_{res['code']}", line=location['line'])
return list(createResults())

def walkDirectory(directory):
def generate():
for filename in glob.iglob(os.path.join(directory, '**/*.py'), recursive=True):
res = os.path.relpath(filename, directory)
yield res
return list(generate())

def readConfiguration(configFile, srcDir):
def allFiles(): return walkDirectory(srcDir)
try:
configuration = readJsonFile(configFile)
files = configuration.get('files') or allFiles()
except Exception:
files = allFiles()
return [f for f in files if isPython3(f)]

def chunks(lst,n):
return [lst[i:i + n] for i in range(0, len(lst), n)]

def runProspectorWith(files, cwd):
res = runProspector([
'--output-format=json',
'--with-tool=frosted',
'--with-tool=vulture',
'--with-tool=pyroma',
'--with-tool=mypy'],
files,
cwd)
return parseResult(res)

def runTool(configFile, srcDir):
files = readConfiguration(configFile, srcDir)
res = []
filesWithPath = [os.path.join(srcDir,f) for f in files]
for chunk in chunks(filesWithPath, 10):
res.extend(runProspectorWith(chunk, srcDir))
for result in res:
if result.filename.startswith(srcDir):
result.filename = os.path.relpath(result.filename, srcDir)
return res

def resultsToJson(results):
return os.linesep.join([toJson(res) for res in results])

if __name__ == '__main__':
with timeout(getTimeout(os.environ.get('TIMEOUT') or '')):
try:
results = runTool('/.codacyrc', '/src')
print(resultsToJson(results))
except Exception:
traceback.print_exc()
sys.exit(1)
Loading

0 comments on commit 83fdbc0

Please sign in to comment.