Skip to content

Commit

Permalink
initial public landing
Browse files Browse the repository at this point in the history
  • Loading branch information
Pike committed Mar 25, 2012
0 parents commit 26f3eed
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
*.pyc
MANIFEST
build
dist
27 changes: 27 additions & 0 deletions README.md
@@ -0,0 +1,27 @@
Ever tried to refactor some python code and trying to find out where you're
code is used?

find . -name \*.py | xargs grep foo.bar

is kinda nice, but it'll find imports, comments, and whatnot.

Meet pygrep, it allows you to find all references to your code,

pygrep foo.bar some/dir other/stuff.py

That will show you some output like

file.py(Class.function):lineno foo.bar.full.identifier

In `file.py`, there's a reference to `foo.bar.full.identifier` in the method
`function` in class `Class`.

Supported
---------
At this point, pygrep resolves

from foo import bar

and

from foo import bar as baz
78 changes: 78 additions & 0 deletions pygrep/__init__.py
@@ -0,0 +1,78 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.


import ast
from collections import deque
from os import walk
import os.path


class IdentVisitor(ast.NodeVisitor):
def __init__(self, path, ident):
super(IdentVisitor, self).__init__()
self.path = path
self.ident = ident.split('.')
self.attrs = deque()
self.context = deque()
self.importMap = {}

def visit_ImportFrom(self, node):
if node.level != 0:
# relative imports not supported
return
mods = tuple(node.module.split('.'))
for l, r in zip(mods, self.ident):
if l != r:
return
subident = self.ident[len(mods):]
for alias in node.names:
for l, r in zip(alias.name.split('.'), subident):
if l != r:
continue
lname = alias.asname is not None and alias.asname or alias.name
self.importMap[lname] = mods + (alias.name, )

def context_visit(self, node):
self.context.append((node.name, node.lineno))
self.generic_visit(node)
self.context.pop()
visit_FunctionDef = visit_ClassDef = context_visit

def visit_Name(self, node):
ident = tuple(self.attrs) + (node.id,)
self.attrs.clear()
if ident[0] in self.importMap:
ident = self.importMap[ident[0]] + ident[1:]
if len(ident) < len(self.ident):
return
for l, r in zip(ident, self.ident):
if l != r:
return
fd = ''
if self.context:
fd = '(%s)' % '.'.join(map(lambda t: t[0], self.context))
print '%s%s:%s\t%s' % (self.path, fd, node.lineno, '.'.join(ident))
def visit_Attribute(self, node):
self.attrs.appendleft(node.attr)
self.generic_visit(node)


class handleIdent:
def __init__(self, ident, files_or_dirs):
self.ident = ident
for fd in files_or_dirs:
if os.path.isdir(fd):
for dirpath, dirnames, filenames in walk(fd):
for fn in filenames:
if fn.endswith('.py'):
self.handleFile(os.path.join(dirpath, fn))
else:
self.handleFile(fd)
def handleFile(self, path):
m = ast.parse(open(path).read(), path)
iv = IdentVisitor(path, self.ident)
iv.visit(m)


14 changes: 14 additions & 0 deletions scripts/pygrep
@@ -0,0 +1,14 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

import pygrep
from optparse import OptionParser


parser = OptionParser()
(options, args) = parser.parse_args()
ident = args[0]
files_or_dirs = args[1:]
pygrep.handleIdent(ident, files_or_dirs)
13 changes: 13 additions & 0 deletions setup.py
@@ -0,0 +1,13 @@
#!/usr/bin/env python

from distutils.core import setup

setup(name='pygrep',
version='0.1',
description='Find python identifiers',
author='Axel Hecht',
author_email='axel@pike.org',
url='http://github.com/Pike/pygrep',
packages=['pygrep'],
scripts=['scripts/pygrep'],
)

0 comments on commit 26f3eed

Please sign in to comment.