diff --git a/gunicorn/six.py b/gunicorn/six.py index e05e25f6..f4fb9b86 100644 --- a/gunicorn/six.py +++ b/gunicorn/six.py @@ -286,6 +286,63 @@ _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") +def _check_if_pyc(fname): + """ Returns True if the extension is .pyc, False if .py and None if otherwise """ + from imp import find_module + from os.path import realpath, dirname, basename, splitext + + # Normalize the file-path for the find_module() + filepath = realpath(fname) + dirpath = dirname(filepath) + module_name = splitext(basename(filepath))[0] + + # Validate and fetch + try: + fileobj, fullpath, (_, _, pytype) = find_module(module_name, [ dirpath ]) + + except ImportError: + raise IOError("Cannot find config file. Path maybe incorrect! : {0}".format(filepath)) + + return (pytype, fileobj, fullpath) + + +def _get_codeobj(pyfile): + """ Returns the code object, given a python file """ + from imp import PY_COMPILED, PY_SOURCE + + result, fileobj, fullpath = _check_if_pyc(pyfile) + + # WARNING: + # fp.read() can blowup if the module is extremely large file. + # Lookout for overflow errors. + try: + data = fileobj.read() + finally: + fileobj.close() + + # This is a .pyc file. Treat accordingly. + if result is PY_COMPILED: + # .pyc format is as follows: + # 0 - 4 bytes: Magic number, which changes with each create of .pyc file. + # First 2 bytes change with each marshal of .pyc file. Last 2 bytes is "\r\n". + # 4 - 8 bytes: Datetime value, when the .py was last changed. + # 8 - EOF: Marshalled code object data. + # So to get code object, just read the 8th byte onwards till EOF, and UN-marshal it. + import marshal + code_obj = marshal.loads(data[8:]) + + elif result is PY_SOURCE: + # This is a .py file. + code_obj = compile(data, fullpath, 'exec') + + else: + # Unsupported extension + raise Exception("Input file is unknown format: {0}".format(fullpath)) + + # Return code object + return code_obj + + if PY3: import builtins @@ -300,7 +357,7 @@ if PY3: print_ = getattr(builtins, "print") def execfile_(fname, *args): - return exec_(compile(open(fname, 'rb').read(), fname, 'exec'), *args) + return exec_(_get_codeobj(fname), *args) del builtins @@ -323,7 +380,10 @@ else: raise tp, value, tb """) - execfile_ = execfile + def execfile_(fname, *args): + """ Overriding PY2 execfile() implementation to support .pyc files """ + return exec_(_get_codeobj(fname), *args) + def print_(*args, **kwargs): """The new-style print function."""