First, you probably don't want to do this. As Martin Peters points out, many things, such as functions and top-level classes, are global.
You can filter this only for non-invoked calls to global variables. Functions, classes, built-in functions, or methods that you import from the C extension module, etc., may be called. This will still not catch cases where, for example, you assign a function to a different name after def . Indeed, everything you come up with will be, at best, very rude guidance, and not what you want to consider as an absolute warning.
Also, no matter how you do it, trying to detect implicit global access but not explicit access (with the global statement) will be very difficult, so hopefully this is not important.
There is no obvious way to detect all implicit uses of global variables at the source level.
However, this is fairly easy to do with reflection inside the interpreter.
The documentation for the inspect module has a nice schedule that shows you standard elements of various types.
This function will provide you with a list of all global names referenced by the associated method, unbound method, function or code object:
def get_globals(thing): thing = getattr(thing, 'im_func', thing) thing = getattr(thing, 'func_code', thing) return thing.co_names
If you want to handle non-calls, you can filter it:
def get_callable_globals(thing): thing = getattr(thing, 'im_func', thing) func_globals = getattr(thing, 'func_globals', {}) thing = getattr(thing, 'func_code', thing) return [name for name in thing.co_names if callable(func_globals.get(name))]
This is not ideal (for example, if the globals function has a custom built-in replacement, we will not look for it correctly), but it is probably good enough.
A simple example of its use:
>>> def foo(myparam): ... myglobal ... mylocal = 1 >>> print get_globals(foo) ('myglobal',)
And you can easily compose the import module and recursively lay out your callables and call get_globals() for each of them, which will work for the main cases (top-level functions and methods of top-level and nested classes), although it will not work for anything, dynamically defined (for example, functions or classes defined inside functions).
If you only care about CPython, another option is to use the dis module to scan the entire bytecode in the or module. pyc file (or class or something else) and write down each LOAD_GLOBAL op.
One of the main advantages of this inspect method is that it will find functions that have been compiled, even if they have not been created yet.
The disadvantage is that there is no way to search for names (how could it be if some of them are not already created?), So you cannot easily filter the calling files. You can try to do something fantastic, for example, connect LOAD_GLOBAL ops to the corresponding CALL_FUNCTION (and related) operations, but ... it starts to get complicated.
Finally, if you want to dynamically connect things, you can always replace globals with a shell that warns every time you access it. For instance:
class GlobalsWrapper(collections.MutableMapping): def __init__(self, globaldict): self.globaldict = globaldict
Again, you can easily filter non-contact files:
def __getitem__(self, key): value = self.globaldict[key] if not callable(value): print >>sys.stderr, 'Warning: accessing global "{}"'.format(key) return value
You can also easily throw an exception instead of a warning.
You can associate this with your code in various ways. The most obvious is import capture, which gives each new GlobalsWrapper module around its normally built globals . Although I'm not sure how this will interact with the C extension modules, I assume that it will either work, be ignored harmlessly, or it is probably fine. The only problem is that this will not affect your top level script. If this is important, you can write a shell script that execfile main script with GlobalsWrapper , or something like that.