So this is the best solution I've found so far.
import ast
import hashlib
import inspect
def _remove_docstring(node):
'''
Removes all the doc strings in a FunctionDef or ClassDef as node.
Arguments:
node (ast.FunctionDef or ast.ClassDef): The node whose docstrings to
remove.
'''
if not (isinstance(node, ast.FunctionDef) or
isinstance(node, ast.ClassDef)):
return
if len(node.body) != 0:
docstr = node.body[0]
if isinstance(docstr, ast.Expr) and isinstance(docstr.value, ast.Str):
node.body.pop(0)
#-------------------------------------------------------------------------------
def hash_function(func):
'''
Produces a hash for the code in the given function.
Arguments:
func (types.FunctionObject): The function to produce a hash for
'''
func_str = inspect.getsource(func)
module = ast.parse(func_str)
assert len(module.body) == 1 and isinstance(module.body[0], ast.FunctionDef)
# Clear function name so it doesn't affect the hash
func_node = module.body[0]
func_node.name = ""
# Clear all the doc strings
for node in ast.walk(module):
_remove_docstring(node)
# Convert the ast to a string for hashing
ast_str = ast.dump(module, annotate_fields=False)
# Produce the hash
fhash = hashlib.sha256(ast_str)
result = fhash.hexdigest()
return result
#-------------------------------------------------------------------------------
# Function 1
def test(blah):
'This is a test'
class Test(object):
'''
My test class
'''
print blah
def sub_function(foo):
'''arg'''
print hash_function(test)
#-------------------------------------------------------------------------------
# Function 2
def test2(blah):
'This is a test'
class Test(object):
'''
My test class
'''
print blah
def sub_function(foo):
'''arg meh'''
print hash_function(test2)