Python动态ABC子代创建和Flask

时间:2019-03-04 21:33:17

标签: flask python-3.6 abc

我正在尝试动态执行一些用户提供的代码,该代码定义了两种方法。 on_datainitialise。 (该代码旨在由用户在自己的计算机上本地运行,因此安全性无关紧要。)

为此,我使用exec执行代码字符串,这按预期会导致用户代码中定义的方法位于本地范围内。使用dir()确认了这一点:

['db_session', 'initialise', 'on_data', 'req', 'strategy_id', 'strategy_rec']

您可以看到存在初始化和on_data。跳到下一行,我尝试动态创建任务并分配这两种方法,然后收到此错误:

        exec(strategy_rec.execution_code)
        print(dir())
        UserStrat = type("UserStrat", (core.strategy.ABStrategy, ), {
>           "initialise": initialise,
            "on_data": on_data
        })
E       NameError: name 'initialise' is not defined


我对此有些茫然。我认为这可能是某处发生unicode变量名称的唯一原因,但我已经重写了几次标识符。

任何指针都会很好。这也适用于一个开源项目:https://github.com/octible/algobox/tree/master/service_backtest

这是发生错误的函数代码:

def strategy_execute(strategy_id):
    db_session = give_session()

    req = GetOrThrowDict(request.get_json(force=True))
    strategy_rec = db_session.query(Strategy).filter_by(id=strategy_id).one()

    # try:
    exec(strategy_rec.execution_code)
    print(dir())
    # outs >>> ['db_session', 'initialise', 'on_data', 'req', 'strategy_id', 'strategy_rec']
    UserStrat = type("UserStrat", (core.strategy.ABStrategy, ), {
        "initialise": initialise,
        "on_data": on_data
    })

这是测试的代码:

def test_run_algorithm(client, psql):
    strat_code = """
def initialise(self):
    pass

def on_data(self, context, update):
    return {"signal": core.signal.random()}
"""

    create_response = client.post("/strategy/", json={
        "name": "Lambo by Now",
        "execution_code": strat_code,
        "data_format": "CANDLE",
        "subscribes_to": ["GDAX:BTC-USD:5M"],
        "lookback_period": 0
    })

    created_dict = create_response.json
    print(created_dict)

    assert create_response.status_code == 201

    data = {"context": [], "update": {
            "datetime": 1551398400,
            "open": 100,
            "close": 101,
            "high": 102,
            "low": 98.20,
            "topic": "GDAX:BTC-USD:5M"
        }
    }

    call_response = client.post("/strategy/execute/{}".format(created_dict["id"]),
    json=data)
    import pdb; pdb.set_trace()

    assert "signal" in call_response.json

这是完整的pytest输出:

========================================== test session starts ==========================================
platform linux -- Python 3.6.8, pytest-4.3.0, py-1.7.0, pluggy-0.8.1
rootdir: /home/callam/Documents/projects/algobox, inifile:
plugins: cov-2.6.1
collected 3 items                                                                                       

service_strategy/test/integration/test_algorithm.py ..F                                           [100%]

=============================================== FAILURES ================================================
__________________________________________ test_run_algorithm ___________________________________________

client = <FlaskClient <Flask 'service'>>
psql = {'container': <testcontainers.postgres.PostgresContainer object at 0x7f58f5b6de80>, 'session': <sqlalchemy.orm.session.Session object at 0x7f58f5ad0320>}

    def test_run_algorithm(client, psql):
        strat_code = """
    def initialise(self):
        pass

    def on_data(self, context, update):
        return {"signal": core.signal.random()}
    """

        create_response = client.post("/strategy/", json={
            "name": "Lambo by Now",
            "execution_code": strat_code,
            "data_format": "CANDLE",
            "subscribes_to": ["GDAX:BTC-USD:5M"],
            "lookback_period": 0
        })

        created_dict = create_response.json
        print(created_dict)

        assert create_response.status_code == 201

        data = {"context": [], "update": {
                "datetime": 1551398400,
                "open": 100,
                "close": 101,
                "high": 102,
                "low": 98.20,
                "topic": "GDAX:BTC-USD:5M"
            }
        }

        call_response = client.post("/strategy/execute/{}".format(created_dict["id"]),
>       json=data)

service_strategy/test/integration/test_algorithm.py:112: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../venvs/algobox/lib/python3.6/site-packages/werkzeug/test.py:840: in post
    return self.open(*args, **kw)
../../venvs/algobox/lib/python3.6/site-packages/flask/testing.py:200: in open
    follow_redirects=follow_redirects
../../venvs/algobox/lib/python3.6/site-packages/werkzeug/test.py:803: in open
    response = self.run_wsgi_app(environ, buffered=buffered)
../../venvs/algobox/lib/python3.6/site-packages/werkzeug/test.py:716: in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
../../venvs/algobox/lib/python3.6/site-packages/werkzeug/test.py:923: in run_wsgi_app
    app_rv = app(environ, start_response)
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:2309: in __call__
    return self.wsgi_app(environ, start_response)
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:2295: in wsgi_app
    response = self.handle_exception(e)
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:1741: in handle_exception
    reraise(exc_type, exc_value, tb)
../../venvs/algobox/lib/python3.6/site-packages/flask/_compat.py:35: in reraise
    raise value
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:2292: in wsgi_app
    response = self.full_dispatch_request()
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:1815: in full_dispatch_request
    rv = self.handle_user_exception(e)
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:1718: in handle_user_exception
    reraise(exc_type, exc_value, tb)
../../venvs/algobox/lib/python3.6/site-packages/flask/_compat.py:35: in reraise
    raise value
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:1813: in full_dispatch_request
    rv = self.dispatch_request()
../../venvs/algobox/lib/python3.6/site-packages/flask/app.py:1799: in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

strategy_id = 'e542039f-b1f1-4387-af83-2fb763c79717'

    @strategy_bp.route("/execute/<strategy_id>", methods=["POST"])
    def strategy_execute(strategy_id):
        """
        This is a bit like the observer pattern but with multiple services involved.
        Expects to receive the following from the data or backtesting service.

        {
            "update": {},
            "context": [],
            "topic": "GDAX:BTC-USD:5M",
        }
        """
        db_session = give_session()

        req = GetOrThrowDict(request.get_json(force=True))
        strategy_rec = db_session.query(Strategy).filter_by(id=strategy_id).one()

        # try:
        exec(strategy_rec.execution_code)
        print(dir())
        UserStrat = type("UserStrat", (core.strategy.ABStrategy, ), {
>           "initialise": initialise,
            "on_data": on_data
        })
E       NameError: name 'initialise' is not defined

service_strategy/service/strategy.py:123: NameError
----------------------------------------- Captured stdout setup -----------------------------------------

Pulling image postgres:9.6

Container started:  bcd9f1e79f
Waiting to be ready...
----------------------------------------- Captured stdout call ------------------------------------------
{'active': True, 'data_format': 'CANDLE', 'execution_code': '\ndef initialise(self):\n    pass\n\ndef on_data(self, context, update):\n    return {"signal": core.signal.random()}\n', 'id': 'e542039f-b1f1-4387-af83-2fb763c79717', 'lookback_period': 30, 'name': 'Lambo by Now', 'subscribes_to': '{GDAX:BTC-USD:5M}'}
['db_session', 'initialise', 'on_data', 'req', 'strategy_id', 'strategy_rec']
=========================================== warnings summary ============================================
/home/callam/Documents/venvs/algobox/lib/python3.6/site-packages/psycopg2/__init__.py:144
  /home/callam/Documents/venvs/algobox/lib/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pip install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
    """)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
============================ 1 failed, 2 passed, 1 warnings in 15.04 seconds ============================

0 个答案:

没有答案