Fix upload cancellation (#4527)
* Improve assertions in test_cancel_upload * Fix upload cancellation by using `refs` instead of `upload_controllers`
This commit is contained in:
parent
adfda8adfd
commit
2d9849e00a
@ -40,9 +40,6 @@ let event_processing = false;
|
|||||||
// Array holding pending events to be processed.
|
// Array holding pending events to be processed.
|
||||||
const event_queue = [];
|
const event_queue = [];
|
||||||
|
|
||||||
// Pending upload promises, by id
|
|
||||||
const upload_controllers = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a UUID (Used for session tokens).
|
* Generate a UUID (Used for session tokens).
|
||||||
* Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
|
* Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
|
||||||
@ -486,7 +483,9 @@ export const uploadFiles = async (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upload_controllers[upload_id]) {
|
const upload_ref_name = `__upload_controllers_${upload_id}`
|
||||||
|
|
||||||
|
if (refs[upload_ref_name]) {
|
||||||
console.log("Upload already in progress for ", upload_id);
|
console.log("Upload already in progress for ", upload_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -546,7 +545,7 @@ export const uploadFiles = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Send the file to the server.
|
// Send the file to the server.
|
||||||
upload_controllers[upload_id] = controller;
|
refs[upload_ref_name] = controller;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await axios.post(getBackendURL(UPLOADURL), formdata, config);
|
return await axios.post(getBackendURL(UPLOADURL), formdata, config);
|
||||||
@ -566,7 +565,7 @@ export const uploadFiles = async (
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
delete upload_controllers[upload_id];
|
delete refs[upload_ref_name];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ from reflex.event import (
|
|||||||
from reflex.utils import format
|
from reflex.utils import format
|
||||||
from reflex.utils.imports import ImportVar
|
from reflex.utils.imports import ImportVar
|
||||||
from reflex.vars import VarData
|
from reflex.vars import VarData
|
||||||
from reflex.vars.base import CallableVar, LiteralVar, Var, get_unique_variable_name
|
from reflex.vars.base import CallableVar, Var, get_unique_variable_name
|
||||||
from reflex.vars.sequence import LiteralStringVar
|
from reflex.vars.sequence import LiteralStringVar
|
||||||
|
|
||||||
DEFAULT_UPLOAD_ID: str = "default"
|
DEFAULT_UPLOAD_ID: str = "default"
|
||||||
@ -108,7 +108,8 @@ def clear_selected_files(id_: str = DEFAULT_UPLOAD_ID) -> EventSpec:
|
|||||||
# UploadFilesProvider assigns a special function to clear selected files
|
# UploadFilesProvider assigns a special function to clear selected files
|
||||||
# into the shared global refs object to make it accessible outside a React
|
# into the shared global refs object to make it accessible outside a React
|
||||||
# component via `run_script` (otherwise backend could never clear files).
|
# component via `run_script` (otherwise backend could never clear files).
|
||||||
return run_script(f"refs['__clear_selected_files']({id_!r})")
|
func = Var("__clear_selected_files")._as_ref()
|
||||||
|
return run_script(f"{func}({id_!r})")
|
||||||
|
|
||||||
|
|
||||||
def cancel_upload(upload_id: str) -> EventSpec:
|
def cancel_upload(upload_id: str) -> EventSpec:
|
||||||
@ -120,7 +121,8 @@ def cancel_upload(upload_id: str) -> EventSpec:
|
|||||||
Returns:
|
Returns:
|
||||||
An event spec that cancels the upload when triggered.
|
An event spec that cancels the upload when triggered.
|
||||||
"""
|
"""
|
||||||
return run_script(f"upload_controllers[{LiteralVar.create(upload_id)!s}]?.abort()")
|
controller = Var(f"__upload_controllers_{upload_id}")._as_ref()
|
||||||
|
return run_script(f"{controller}?.abort()")
|
||||||
|
|
||||||
|
|
||||||
def get_upload_dir() -> Path:
|
def get_upload_dir() -> Path:
|
||||||
|
@ -381,9 +381,22 @@ async def test_cancel_upload(tmp_path, upload_file: AppHarness, driver: WebDrive
|
|||||||
await asyncio.sleep(0.3)
|
await asyncio.sleep(0.3)
|
||||||
cancel_button.click()
|
cancel_button.click()
|
||||||
|
|
||||||
# look up the backend state and assert on progress
|
# Wait a bit for the upload to get cancelled.
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
|
# Get interim progress dicts saved in the on_upload_progress handler.
|
||||||
|
async def _progress_dicts():
|
||||||
|
state = await upload_file.get_state(substate_token)
|
||||||
|
return state.substates[state_name].progress_dicts
|
||||||
|
|
||||||
|
# We should have _some_ progress
|
||||||
|
assert await AppHarness._poll_for_async(_progress_dicts)
|
||||||
|
|
||||||
|
# But there should never be a final progress record for a cancelled upload.
|
||||||
|
for p in await _progress_dicts():
|
||||||
|
assert p["progress"] != 1
|
||||||
|
|
||||||
state = await upload_file.get_state(substate_token)
|
state = await upload_file.get_state(substate_token)
|
||||||
assert state.substates[state_name].progress_dicts
|
|
||||||
file_data = state.substates[state_name]._file_data
|
file_data = state.substates[state_name]._file_data
|
||||||
assert isinstance(file_data, dict)
|
assert isinstance(file_data, dict)
|
||||||
normalized_file_data = {Path(k).name: v for k, v in file_data.items()}
|
normalized_file_data = {Path(k).name: v for k, v in file_data.items()}
|
||||||
|
Loading…
Reference in New Issue
Block a user