Я пытаюсь динамически выполнить некоторый предоставленный пользователем код, который определяет два метода.on_data
и initialise
.(Код предназначен для локального запуска пользователем на его собственной машине, поэтому безопасность не имеет значения).
Для этой цели я использую 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
Я немного растерялся по этому вопросу.Единственная причина, по которой я думаю, это может происходить где-то с именами переменных Юникода, но я переписывал идентификаторы несколько раз.
Любые указатели были бы хорошими.Также это для проекта с открытым исходным кодом: 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 ============================