PK ¨‰THË&ìÕ> > ' pygemony-0.1.0.data/scripts/pygemony.pyimport argparse
from pyg.Pygemony import Pygemony
from pyg.utils import get_git_info
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--token', required=True)
parser.add_argument('--username', required=True)
parser.add_argument('--owner')
parser.add_argument('--repo')
args = parser.parse_args()
args = vars(args)
pygemony = Pygemony(args.get('username'),
args.get('token'),
args.get('owner'),
args.get('repo'))
pygemony.run()
PK ¨‰THD„maQ Q pyg/languages.py__author__ = 'Ian'
class LanguageCPP:
def __init__(self):
self.single_comment = '//'
self.multi_comment = ['/*', '*/']
self.file_exts = ['*.cpp', '*.cxx', '*.c', '*.hpp', '*.hxx', '*.h']
self.ignore_dir = []
class LanguageC:
def __init__(self):
self.single_comment = '//'
self.multi_comment = ['/*', '*/']
self.file_exts = ['*.c', '*.h']
self.ignore_dir = []
class LanguagePython:
def __init__(self):
self.single_comment = '#'
self.multi_comment = ['"""', '"""']
# How does iron python, stackless, &c do it?
self.file_exts = ['*.py']
self.ignore_dir = []
class LanguageJavascript:
def __init__(self):
self.single_comment = '//'
self.multi_comment = ['/*', '*/']
self.file_exts = ['*.js', '.node']
self.ignore_dir = ['node_modules']
self.ignore_dir = []
class LanguageGo:
def __init__(self):
self.single_comment = '//'
self.multi_comment = ['/*', '*/']
self.file_exts = ['*.go']
self.ignore_dir = []
PK ¨‰TH pyg/__init__.pyPK ¨‰THÓ
( pyg/utils.pyfrom mimetypes import guess_type
"""
Basically this is a file that contains random stuff that we need but
doesn't really fit with the general scheme of the other files
"""
def get_git_info():
repo = ''
with open('.git/config') as f:
for line in f.readlines():
if 'url' in line:
repo = line.replace('url = ', '').strip()
r = repo.split('/')
# Return a tuple containing the owner and the repo name
return r[-2], r[-1]
def detect_mimetype(file_):
return guess_type(file_)
PK -¿UHÓ´wä
pyg/github.pyimport github3
from os import path
class GithubAPIManager:
# repo_location = grappigpanda/pygemony.py
def __init__(self, user, token, owner, repo):
self.is_authed = False
# Auth stuff
self.user = user
self.token = token
self.gh = self.login()
if self.gh is None:
raise Exception("Failed to login")
# Remote repo stuff
if not owner:
self.owner = self.get_owner().rstrip()
else:
self.owner = owner
if not repo:
self.repo = self.get_repo().rstrip()
else:
self.repo = repo
self.curr_repo = self.gh.repository(str(self.owner), str(self.repo))
def login(self):
try:
return github3.login(self.user, self.token)
except github3.models.GitHubError as e:
print "Failed to login due to {}".format(e)
return None
def _save_submitted_todo(self, issue):
if not path.isfile('./.pyg-submitted'):
with open('./.pyg-submitted', 'a+') as f:
f.write("""
This file was auto-generated by pygemony to help users keep-track of long-forgotten TODOs.
For more information, please visit: https://github.com/GrappigPanda/pygemony
""")
f.write(issue[3])
f.write('\n')
return True
if issue[3] in open('./.pyg-submitted').read():
return False
else:
with open('./.pyg-submitted', 'ab') as f:
f.write(issue[3])
f.write('\n')
return True
def commit(self, todo_found):
# TODO(ian): Assign issues if () in line. (ian), for example
for issue in todo_found:
if self._save_submitted_todo(issue):
self._pprint(issue)
self.curr_repo.create_issue(title=issue[2],
body=self._construct_issue_body(issue))
def get_languages(self):
for i in self.curr_repo.iter_languages():
yield i
def _get_repo_owner(self):
# TODO(ian): Remove the magic directory!
with open('./.git/config', 'r+') as f:
for line in f.readlines():
if 'url = ' in line:
return line.split('github.com/')[-1].split('/')
def get_repo(self):
return self._get_repo_owner()[1]
def get_owner(self):
return self._get_repo_owner()[0]
@staticmethod
def _construct_issue_body(issue):
# TODO(ian): Move all @staticmethods to a seperate class
sz = 'File Location: {}
Line Number: {}'.format(issue[0], issue[1])
sz += '
This message was auto-generated by Pygemony: '
sz += 'Github'
return sz
def _pprint(self, issue):
msg = "Committing to repo: {}"
msg += "\n\tFile Name: {}:{}\n\tTodo Message:{}"
print msg.format(self.repo, issue[0], issue[1], issue[2])
PK -¿UHl,½ô| | pyg/Pygemony.pyfrom fnmatch import filter
from os import walk, path
import hashlib
from utils import detect_mimetype
from github import GithubAPIManager
from languages import *
class Pygemony:
def __init__(self, user=None, token=None, owner=None, repo=None):
# todo_found contains a list of the following layout:
# ['file_path', 'line_number', 'todo_message', 'md5 of todo']
self.blacklist = ['build', '.git']
self.todo_found = []
self.github = GithubAPIManager(user, token, owner, repo)
# TODO(ian): Add support for parsing more than one file type
self.language = self.lookup_language()
def find_end_comment(self, f):
# TODO(ian): Remove this function as we no longer support multiline TODO
todo_content = []
x = f
count = 0
for line in x.readlines():
todo_content.append(line)
if self.language.multi_comment[1] in line:
return todo_content
if count > 20:
return None
todo_content.append(line)
count += 1
def _sanitize_todo_line(self, lines):
# We're mainly aiming to remove newlines and tab characters here.
lines = lines.replace('\n', '')
while ' ' in lines or '\t' in lines:
lines = lines.replace(' ', '')
for lang in self.language:
lines = lines.replace(lang.single_comment, '')
return lines
@staticmethod
def hash_todo(todo_content, line_number, file_name):
m = hashlib.md5()
m.update('{}-{}'.format(todo_content, file_name))
return str(m.hexdigest())
def parse_for_todo(self, f, file_):
for i, line in enumerate(f.readlines()):
if "TODO" in line and self._starts_with_comment(line):
line = self._sanitize_todo_line(line)
self.todo_found.append([file_, i, line, self.hash_todo(line, i, file_)])
def parse_by_extension(self, files):
for lang in self.language:
for ext in lang.file_exts:
for file_ in filter(files, ext):
yield file_
def find_all_files(self, root):
files_found = []
for roots, dirs, files in walk(root):
base_dir = roots.split('/')[1]
if base_dir not in self.blacklist:
for file_ in self.parse_by_extension(files):
files_found.append(path.join(roots, file_))
return files_found
def file_handler(self):
# First we need to remove any non-text files
files_found = self.find_all_files('./')
# TODO(ian): filter() over files to parse out by mimetype
for file_ in files_found:
file_type = detect_mimetype(file_)
# We're looking for startswith('text/'). Mimetype returns
# None if it can't determine file type. Remove if either is True
try:
if file_type[0].startswith("application") or file_type[0] is None:
files_found.remove(file_)
except (AttributeError, IndexError) as e:
print "Failed to open file {} with error of {}".format(file_, e)
for file_ in files_found:
try:
with open(file_, 'r') as f:
self.parse_for_todo(f, file_)
except IOError as e:
print "Failed to open file {} with error of {}".format(file_, e)
return files_found
def run(self):
self.file_handler()
self.github.commit(self.todo_found)
def lookup_language(self):
lang_map = {'cpp': LanguageCPP,
'python': LanguagePython,
'javascript': LanguageJavascript,
'c': LanguageC,
'go': LanguageGo}
langs = [i for i in self.github.get_languages()]
for i in langs:
self.blacklist.append(lang_map[str(langs[0][0]).lower()]().ignore_dir)
return [lang_map[str(langs[0][0]).lower()]()]
def _starts_with_comment(self, line):
comments = self._create_comment_start_list()
for comment in comments:
if line.startswith(comment):
return True
def _create_comment_start_list(self):
comments = []
for lang in self.language:
comments.append(lang.single_comment)
comments.append(lang.multi_comment[0])
return comments
PK °VHS”Ö” ” ( pygemony-0.1.0.dist-info/DESCRIPTION.rst
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
PK °VHÈœëp p &