解决flask一个bug的思路

场景是我需要hook一些python的函数做代码,需要设置PYTHONPATH环境变量,但是设置PYTHONPATH之后,在有些地方出现函数没有hook到的问题
首先文件结构为
├── main.py
├── route.py
├── pages
│—├── test.py

main.py文件

import sys
from web_ui.flask_app import app
from web_ui.pages import __all__ as all_page

for page in all_page:
    try:
        # Page import test
        __import__('pages.' + page, globals(), locals(), ['*'], -1)
    except Exception as e:
        pass

if __name__ == '__main__':
    app.run(app.run(host='0.0.0.0'))

route.py

from flask import request, redirect, abort
from flask.exceptions import JSONBadRequest

def route(rule, web_app=None, validator=None, check_csrf=None, permissions=None, **options):
    if not web_app:
        from web_ui.flask_app import app
        web_app = app

    @web_app.route(rule, **options)
    def __route(*args, **kwargs):
            ...........

test.py

from route import route
from flask import request
from flask import redirect
from flask import Response
import subprocess

@route('/test', methods=['POST'])
def test():
    proc = subprocess.call('ls')

利用的hook脚本:
subprocess.py

import sys
import io
import os

##########
# Consts #
##########

_HOOK_TEMPLATES_DICT = \
{
"_hook_%(name)s":
"""
def _hook_%(name)s(self, *args, **kwargs):
    #print "\\nCLS HOOK: %%s.%(name)s(args=%%s, kwargs=%%s) = ..." %% (__name__, args, kwargs)
    _hook_args = args
    _hook_kwargs = kwargs
    (_hook_args, _hook_kwargs) = self._pre_%(name)s_hook(*args, **kwargs)
    #print "\\tCalling _pre_%(name)s_hook(%%s, %%s) = (%%s, %%s)" %% (args, kwargs, _hook_args, _hook_kwargs)
    retval = object.__getattribute__(self, '%(name)s')(*_hook_args, **_hook_kwargs)
    #print "\\tCalling _post_%(name)s(args=%%s, kwargs=%%s) = %%s" %% (_hook_args, _hook_kwargs, str(retval))
    retval = self._post_%(name)s_hook(retval, *_hook_args, **_hook_kwargs)
    #print "= %%s" %% (str(retval))
    return retval
"""
,
"_pre_%(name)s_hook":
"""
def _pre_%(name)s_hook(self, *args, **kwargs):
    return (args, kwargs)
"""
,
"_post_%(name)s_hook":
"""
def _post_%(name)s_hook(self, retval, *args, **kwargs):
    return retval
"""
}

###########
# Classes #
###########

class _CustomGetAttribute:
    def __getattribute__(self, name):
        retval = object.__getattribute__(self, name)

        # "Magic" Objects / Weak "Internal Use" Indicator? AS IS!
        if name.startswith('_'):
            return retval

        # Callable? Hook!
        if callable(retval):
            try:
                return object.__getattribute__(self, '_hook_' + name)
            except AttributeError:
                import types

                # i.e. ("_hook_%(name)s", "def _hook_%(name)s(self, *args, **kwargs): ..."
                for fcn_template_name, fcn_template_code in _HOOK_TEMPLATES_DICT.iteritems():
                    fcn_name = fcn_template_name % {'name': name}

                    # No such hook function? Create it!
                    if not hasattr(self, fcn_name):
                        fcn_code = fcn_template_code % {'name': name}
                        if self.__trace__ == True:
                            fcn_code = fcn_code.replace('#print', 'print')
                        exec fcn_code
                        setattr(self, fcn_name, types.MethodType(locals()[fcn_name], self))

            return object.__getattribute__(self, '_hook_' + name)

        return retval


class _InstallClsHook(type):
    def __new__(meta, name, bases, dct):
        try:
            bases = (_CustomGetAttribute,) + bases + (getattr(sys.modules[__name__],name),)
        except:
            pass
        return type.__new__(meta, name, bases, dct)


class _InstallFcnHook(object):
    def __init__(self, fcn, debug=False):
        self.debug = debug
        self._fcn = fcn

    def _pre_hook(self, *args, **kwargs):
        print args
        with io.open('/tmp/subprocess.log', 'ab+') as file:
            file.write(str(args)+"\n")
        return (args, kwargs)

    def _post_hook(self, retval, *args, **kwargs):
        return retval

    def __call__(self, *args, **kwargs):
        if self.debug:
            print "\nFCN HOOK: %s(args=%s, kwargs=%s) = ..." % (self._fcn.__name__, args, kwargs)

        _hook_args = args
        _hook_kwargs = kwargs
        (_hook_args, _hook_kwargs) = self._pre_hook(*args, **kwargs)

        if self.debug:
            print "\tCalling _pre_hook(%s, %s) = (%s, %s)" % (args, kwargs, _hook_args, _hook_kwargs)

        retval = self._fcn(*_hook_args, **_hook_kwargs)

        if self.debug:
            print "\tCalling _post_hook(args=%s, kwargs=%s) = %s" % (_hook_args, _hook_kwargs, str(retval))

        retval = self._post_hook(retval, *_hook_args, **_hook_kwargs)

        if self.debug:
            print "= %s" % (str(retval))

        return retval

#############
# Functions #
#############

def _load_and_register_as(input_modname, output_modnames=[], look_path=[]):
    import imp
    mod = None
    fd = None
    try:
        fd, pathname, description = imp.find_module(input_modname, look_path)
        for output_name in output_modnames:
            mod = imp.load_module(output_name, fd, pathname, description)
    finally:
        if fd is not None:
            fd.close()
    return mod

解决过程:

由于无法有效的hook到test函数,所以首先输出env来查看环境变量是否加载,修改test.py的test函数为:

def test():
    subprocess.call('env')
    subprocess.call('ls')

查看输出之后,发现环境变量PYTHONPATH已经加载

再查看这个函数的调用堆栈,我在 https://www.cnblogs.com/tuzkee/archive/2013/08/07/3243110.html 这里找到一个显示函数调用的堆栈的代码,于是我把test函数改成

def test():

    for _ in xrange(0,4):
       print "----------------------------------------------------------"
       import sys
       depth=_
       frame = sys._getframe(depth)
       code = frame.f_code
       print "frame depth = ", depth
       print "func name = ", code.co_name
       print "func filename = ", code.co_filename
       print "func lineno = ", code.co_firstlineno
       print "func locals = ", frame.f_locals
       print "-----------------------------------------------------------"


    subprocess.call('env')
    subprocess.call('ls')

通过输出显示出test函数由route.py的route函数调用,而route由flask/app.py调用
然后重点看route.py,在route.py中添加一些通过shell指令输出的函数

from flask import request, redirect, abort
from flask.exceptions import JSONBadRequest

def route(rule, web_app=None, validator=None, check_csrf=None, permissions=None, **options):
    if not web_app:
        from web_ui.flask_app import app
        web_app = app
        subprocess.Popen(['echo','1'])

    @web_app.route(rule, **options)
    def __route(*args, **kwargs):
        subprocess.Popen(['echo','2'])
            ...........

于是就看到在程序启动的时候,输出了很多1,而且也会发现成功hook到echo 1这个shell代码,而通过浏览器访问/test的时候会发现会输出2,但是没有成功hook到echo 2,所以我猜测,在main.py这个文件中通过

__import__('pages.' + page, globals(), locals(), ['*'], -1)

会导入pages文件夹下的模块到内存中,第一次导入模块的时候,会成功hook,之后就会把hook到的函数转到正常的函数中,当用浏览器访问/test的时候,就是访问的正常的函数,就没有经过hook了,所以解决这个bug就应该在main.py导入模块的代码后面卸载掉subprocess这个模块

import sys
from web_ui.flask_app import app
from web_ui.pages import __all__ as all_page

for page in all_page:
    try:
        # Page import test
        __import__('pages.' + page, globals(), locals(), ['*'], -1)
        if 'subprocess' in sys.modules:
            del sys.modules['subprocess']
    except Exception as e:
        pass

if __name__ == '__main__':
    app.run(app.run(host='0.0.0.0'))

所以这个bug就解决了

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niexinming/article/details/80322248
个人分类: 编程
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

解决flask一个bug的思路

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭