Skip to content

Instantly share code, notes, and snippets.

@amcgregor
Last active August 19, 2023 13:22
Show Gist options
  • Save amcgregor/db1ceed405f299b3e283ecb6292e496b to your computer and use it in GitHub Desktop.
Save amcgregor/db1ceed405f299b3e283ecb6292e496b to your computer and use it in GitHub Desktop.
A sample application to try out capturing multi-file uploads with nested structures.

Create a new virtual environment somewhere you'd like to place these files.

python3.9 -m venv uploadtest
cd uploadtest
. bin/activate

Then drop these files in that directory. Install dependencies:

pip3 install -r requirements.txt

Spin up the debugger:

wdb.server.py &

This will potentially produce a fair amount of output, especially once active. We've spawned it in the background, when done, run fg and ⌃C it to stop. Also use ⌃C to stop the development-time web server which we now spin up:

python app.py

Open http://localhost:8080 in a browser, be presented with a file upload form.

I select a folder named sample containing two top-level screenshot PNGs and a subfolder named deeper containing a third screenshot with the date 04-14.

Submitting, I'm dropped into the WDB interactive debugger. We can now explore the uploaded data passed to our endpoint as data.

>>> data
{'files': [
    FieldStorage('files', 'Screen Shot 2022-04-19 at 19.17.52.png'), 
    FieldStorage('files', 'Screen Shot 2022-04-14 at 09.28.50.png'), 
    FieldStorage('files', 'Screen Shot 2022-04-20 at 10.30.09.png')
  ]}

The interesting one is the deeper screenshot, the second element uploaded in this case. (Order is not gauranteed.)

>>> data['files'][1].name, data['files'][1].filename
('files', 'Screen Shot 2022-04-14 at 09.28.50.png')

The field name is correct, and the name contains no path information. Now we can drop the doozie:

>>> data['files'][1].__dict__
...

There will be a lot of data. These are all of the instance–local attributes. We can look in here for the word deeper… which is not found.

from wdb import set_trace
from web.core import Application
class Root:
def __init__(self, context):
self._ctx = context
def __call__(self, **data):
context = self._ctx
request = context.request
if data: set_trace()
context.response.content_type = 'text/html'
return '<!DOCTYPE html><title>Folder Sample</title>' \
'<form method=post enctype=multipart/form-data>' \
'<input type=file name=files webkitdirectory multiple>' \
'<input type=submit>'
if __name__ == '__main__':
Application(Root).serve('wsgiref')
wheel==0.37.1
marrow.package==1.2.0
WebOb==1.8.7
WebCore==2.0.4
web.dispatch.object==2.1.0
click==8.1.2
importmagic3==0.2.0
jedi==0.18.1
log-colorizer==2.0.0
parso==0.8.3
psutil==5.9.0
python-magic==0.4.25
six==1.16.0
spark-parser==1.8.9
tornado==5.1.1
tornado-systemd==1.0.1
uncompyle6==3.8.0
wdb==3.3.0
wdb.server==3.3.0
xdis==6.0.3
@amcgregor
Copy link
Author

The requirements are split between "universal / packaging", "web framework", and "debugger". Clearly, a debugger is a bit more complex than the application we're writing here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment