Skip to content

Commit d019d8f

Browse files
authored
Merge pull request #208 from MetaCell/feature/205_dialog_stacktrace
Feature/205 dialog stacktrace
2 parents 973a240 + 6cb82b2 commit d019d8f

4 files changed

Lines changed: 78 additions & 62 deletions

File tree

webapp/Utils.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { execPythonMessage, evalPythonMessage } from '@geppettoengine/geppetto-client/js/communication/geppettoJupyter/GeppettoJupyterUtils';
1+
import {
2+
execPythonMessage,
3+
evalPythonMessage
4+
} from '@geppettoengine/geppetto-client/js/communication/geppettoJupyter/GeppettoJupyterUtils';
25
import React from 'react';
36

47

@@ -126,7 +129,7 @@ const Utils = {
126129
return name;
127130

128131
},
129-
132+
130133
// FIXME: Hack to remove scaped chars (\\ -> \ and \' -> ') manually
131134
convertToJSON (data){
132135
if (typeof data === 'string' || data instanceof String){
@@ -135,7 +138,11 @@ const Utils = {
135138
return data
136139
},
137140

138-
getErrorResponse (data){
141+
getPlainStackTrace (stackTrace) {
142+
return stackTrace.replace(/\u001b\[.*?m/g, '');
143+
},
144+
145+
getErrorResponse (data) {
139146
var parsedData = this.convertToJSON(data)
140147
if (parsedData.type && parsedData['type'] == 'ERROR') {
141148
const error = { details: parsedData['details'] }
@@ -149,10 +156,6 @@ const Utils = {
149156
return null;
150157
},
151158

152-
parsePythonException (exception){
153-
return <pre style={{ whiteSpace: 'pre-wrap' }} dangerouslySetInnerHTML={{ __html: IPython.utils.fixConsole(exception) }}></pre>
154-
},
155-
156159
handleUpdate (updateCondition, newValue, originalValue, context, componentName) {
157160
if ((updateCondition) && (newValue != originalValue)) {
158161
/*

webapp/components/topbar/dialogs/ActionDialog.js

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
import React from 'react';
22
import Dialog from '@material-ui/core/Dialog/Dialog';
33
import Button from '@material-ui/core/Button';
4-
import Utils from '../../../Utils';
5-
64
import DialogActions from '@material-ui/core/DialogActions';
75
import DialogContent from '@material-ui/core/DialogContent';
86
import DialogTitle from '@material-ui/core/DialogTitle';
7+
import { withStyles } from "@material-ui/core/styles";
8+
import { bgDarkest, fontColor } from "root/theme";
9+
10+
const styles = () => ({
11+
cancel: { marginRight: 10 },
12+
exception: {
13+
whiteSpace: 'pre-wrap',
14+
backgroundColor: bgDarkest,
15+
color: fontColor
16+
},
17+
});
918

10-
const styles = { cancel: { marginRight: 10 } }
11-
export default class ActionDialog extends React.Component {
19+
class ActionDialog extends React.Component {
1220

1321
state = { hide: !this.props.openErrorDialogBox && !this.props.openDialog }
1422

1523
performAction = () => {
1624
if (this.props.command) {
1725
if (this.props.isFormValid === undefined || this.props.isFormValid()) {
1826
GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, this.props.message);
19-
2027
this.props.pythonCall(this.props.command, this.props.args)
21-
2228
}
2329
}
2430
this.setState({ hide: true })
@@ -47,40 +53,39 @@ export default class ActionDialog extends React.Component {
4753
}
4854

4955
render () {
56+
const { classes, errorMessage, errorDetails } = this.props;
57+
58+
let action;
5059
let title
5160
let content
5261

53-
if (this.props.errorMessage === '') {
62+
if (errorMessage === '') {
5463
title = this.props.title
55-
var action = (
64+
action = (
5665
<Button
5766
id="appBarPerformActionButton"
5867
key="appBarPerformActionButton"
5968
variant="contained"
6069
color="primary"
6170
onClick={this.performAction}
6271
>{this.props.buttonLabel}</Button>
63-
)
72+
);
6473

6574
content = this.props.children;
6675
} else {
67-
var action = (
76+
action = (
6877
<Button
6978
variant="contained"
7079
color="primary"
7180
key="BACK"
7281
onClick={() => this.handleClickGoBack()}
7382
>BACK</Button>
74-
)
83+
);
7584

76-
title = "Exception in Python kernel was thrown"
77-
if (this.props.errorMessage != null && typeof this.props.errorMessage == "string") {
78-
title = this.props.errorMessage
79-
} else {
80-
console.warn("Unknown/Undefined object was passed as error message")
85+
title = errorMessage
86+
if (errorDetails) {
87+
content = StackTrace(classes.exception, errorDetails)
8188
}
82-
83-
content = this.props.errorDetails ? Utils.parsePythonException(this.props.errorDetails) : ''
8489
}
8590

8691
return (
@@ -107,4 +112,13 @@ export default class ActionDialog extends React.Component {
107112
</Dialog>
108113
);
109114
}
110-
}
115+
}
116+
117+
const StackTrace = (classes, stackTrace) => (
118+
<pre
119+
className={classes}
120+
dangerouslySetInnerHTML={{ __html: IPython.utils.fixConsole(stackTrace) }}
121+
/>
122+
);
123+
124+
export default withStyles(styles)(ActionDialog);

webapp/redux/middleware/middleware.js

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {
2-
UPDATE_CARDS, CREATE_NETWORK, CREATE_SIMULATE_NETWORK, PYTHON_CALL, SIMULATE_NETWORK, SHOW_NETWORK,
1+
import {
2+
UPDATE_CARDS, CREATE_NETWORK, CREATE_SIMULATE_NETWORK, PYTHON_CALL, SIMULATE_NETWORK, SHOW_NETWORK,
33
editModel, EDIT_MODEL, LOAD_TUTORIAL, RESET_MODEL, setDefaultWidgets
44
} from '../actions/general';
55
import FLEXLAYOUT_DEFAULT_STATE from '../../components/layout/defaultLayout';
@@ -16,32 +16,35 @@ let previousLayout = { edit: undefined, network: undefined };
1616

1717

1818
export default store => next => action => {
19-
2019

2120
const switchLayoutAction = (edit = true, reset = true) => {
2221
previousLayout[store.getState().general.editMode ? 'edit' : 'network'] = store.getState().layout;
2322
if (reset) {
2423
previousLayout = { edit: undefined, network: undefined };
2524
}
26-
return next(edit
27-
? previousLayout.edit ? setLayout(previousLayout.edit) : setWidgets({ ...Constants.EDIT_WIDGETS })
25+
return next(edit
26+
? previousLayout.edit ? setLayout(previousLayout.edit) : setWidgets({ ...Constants.EDIT_WIDGETS })
2827
: previousLayout.network ? setLayout(previousLayout.network) : setWidgets({ ...Constants.DEFAULT_NETWORK_WIDGETS }));
2928
}
29+
3030
const toNetworkCallback = reset => () => {
31-
3231
switchLayoutAction(false, reset);
3332
next(action);
3433
};
3534

36-
const pythonErrorCallback = errorPayload => next(openBackendErrorDialog(errorPayload.message));
35+
const pythonErrorCallback = error => {
36+
console.debug(Utils.getPlainStackTrace(error.errorDetails))
37+
return next(openBackendErrorDialog(error));
38+
};
39+
3740
switch (action.type) {
3841

3942
case UPDATE_CARDS:
4043
console.log("Triggered card update")
4144
next(action);
4245
break;
4346
case SHOW_NETWORK:
44-
47+
4548
switchLayoutAction(false, false);
4649
next(action);
4750
break;
@@ -59,7 +62,7 @@ export default store => next => action => {
5962
);
6063
break
6164
}
62-
case CREATE_NETWORK:{
65+
case CREATE_NETWORK:{
6366
instantiateNetwork({}).then(toNetworkCallback(false), pythonErrorCallback);
6467
break;
6568
}
@@ -82,7 +85,7 @@ export default store => next => action => {
8285
case NETPYNE_COMMANDS.deleteModel:
8386
next(editModel);
8487
switchLayoutAction(true, true);
85-
88+
8689
break;
8790
default:
8891
break;
@@ -95,80 +98,76 @@ export default store => next => action => {
9598
case LOAD_TUTORIAL: {
9699
const tutName = action.payload.replace('.py', '')
97100
GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, `Loading tutorial ${tutName}`);
98-
101+
99102
const params = {
100103
modFolder: 'mod',
101104
loadMod: false,
102105
compileMod: false,
103106

104-
netParamsPath:".",
107+
netParamsPath: ".",
105108
netParamsModuleName: tutName,
106109
netParamsVariable: "netParams",
107110

108-
simConfigPath:".",
111+
simConfigPath: ".",
109112
simConfigModuleName: tutName,
110113
simConfigVariable: "simConfig",
111-
112114
}
113115

114-
pythonCall({ cmd: 'netpyne_geppetto.importModel', args:params })
116+
pythonCall({ cmd: 'netpyne_geppetto.importModel', args: params })
115117
.then(response => console.log(response))
116118
break
117119
}
118120
default: {
119121
next(action);
120122
}
121123
}
122-
123-
124124
}
125125

126-
127126
const instantiateNetwork = payload => createSimulateBackendCall(
128127
NETPYNE_COMMANDS.instantiateModel,
129128
payload,
130129
"The NetPyNE model is getting instantiated...",
131130
GEPPETTO.Resources.INSTANTIATING_MODEL)
132131

133-
134-
const simulateNetwork = payload =>
132+
const simulateNetwork = payload =>
135133
createSimulateBackendCall(
136134
NETPYNE_COMMANDS.simulateModel,
137135
payload,
138136
"The NetPyNE model is getting simulated...",
139137
GEPPETTO.Resources.RUNNING_SIMULATION
140138
)
141139

142-
143140
const createSimulateBackendCall = async (cmd, payload, consoleMessage, spinnerType) => {
144141
GEPPETTO.CommandController.log(consoleMessage);
145142
GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, spinnerType);
146143

147-
148144
const response = await Utils.evalPythonMessage(cmd, [payload]);
149145
console.log('Python response', response);
150146
GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner);
151147
const responsePayload = processError(response);
152148
console.log('Python payload', responsePayload);
149+
153150
if (responsePayload) {
154-
console.error(responsePayload.errorDetails.replace(/\u001b\[.*?m/g, ''))
155-
throw new Error(responsePayload.errorMessage);
151+
throw responsePayload
152+
156153
} else {
157154
GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL);
158-
155+
159156
dehydrateCanvas()
160157

161158
GEPPETTO.Manager.loadModel(response);
162159
GEPPETTO.CommandController.log('Instantiation / Simulation completed.');
163-
164160
}
165161
return response;
166162
}
167163

168164
export const processError = response => {
169-
var parsedResponse = Utils.getErrorResponse(response);
165+
const parsedResponse = Utils.getErrorResponse(response);
170166
if (parsedResponse) {
171-
return { errorMessage: parsedResponse['message'], errorDetails: parsedResponse['details'] }
167+
return {
168+
errorMessage: parsedResponse['message'],
169+
errorDetails: parsedResponse['details']
170+
}
172171
}
173172
return false
174173
}
@@ -177,14 +176,14 @@ const pythonCall = async ({ cmd, args }) => {
177176
const response = await Utils.evalPythonMessage(cmd, [args])
178177
const errorPayload = processError(response);
179178
GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner);
179+
180180
if (errorPayload) {
181-
console.error(errorPayload.errorDetails.replace(/\u001b\[.*?m/g, ''))
182-
throw new Error(errorPayload.errorMessage);
183-
}
181+
throw errorPayload
182+
}
183+
184184
return response;
185185
}
186186

187-
188187
const dehydrateCanvas = () => {
189188
if ('CanvasContainer' in window) {
190189
CanvasContainer.engine.reset()

webapp/redux/reducers/errors.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
import { OPEN_BACKEND_ERROR_DIALOG, CLOSE_BACKEND_ERROR_DIALOG } from '../actions/errors';
33

44
// Default state for general
5-
export const ERROR_DEFAULT_STATE = {
5+
export const ERROR_DEFAULT_STATE = {
66
openDialog: false,
77
errorMessage: '',
88
errorDetails: ''
99
};
1010

11-
1211
// reducer function
1312
export default function reduceError (state = ERROR_DEFAULT_STATE, action) {
1413
switch (action.type) {
1514
case OPEN_BACKEND_ERROR_DIALOG:
16-
return {
15+
return {
1716
...state,
1817
openDialog: true,
19-
errorMessage: action.payload,
18+
errorMessage: action.payload.errorMessage,
19+
errorDetails: action.payload.errorDetails
2020
}
2121
case CLOSE_BACKEND_ERROR_DIALOG:
2222
return { ...state, ...ERROR_DEFAULT_STATE }

0 commit comments

Comments
 (0)