2

The following is my failed attempt at writing HTML/Javascript/PyScript code that allows a user to upload a csv (or excel) file, which is then available for use in PyScript (e.g., Pandas). As you might notice, I am not very experienced with Javascript.

<html>
  <head>
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
        
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
    
    <py-config>
        packages = ["pandas"]
    </py-config>
  </head>
  <body>
    <input type="file" id="fileinput" accept=".csv, .xlsx"/>

    <script>
        var test = "test";

        function assignFile(e) {
            // Assign file to js variable
            window.file = e.target.files[0];
            console.log("File assigned")
        }
    </script>

    <py-script>
import pandas as pd
import js
import pyodide
from js import test
js.console.log(test)

def get_file(e):
    js.console.log("Attempting file upload: " + e.target.value)

    # Assign file to a js variable
    js.assignFile(e)
    # Import variable to python
    from js import file

    display(pd.read_csv(file))

get_file_proxy = pyodide.ffi.create_proxy(get_file)

js.document.getElementById("fileinput").addEventListener("change", get_file_proxy)
    </py-script>
  </body>
</html>

Some notes to this code: I am aware that the part written in the script element could also be written in the py-script element, but decided not to. Furthermore, the line from js import file imports the variable created in Javascript, but when I try to use this variable with Pandas, it gives me the error in the console Invalid file path or buffer object type: <class 'pyodide.JsProxy'>. This is contrary to the properly functioning line from js import test. However, the specific error is unimportant to my question, which is:

What would be a simple way to allow a user to upload a csv (or xlsx) file for use in PyScript?

1 Answer 1

2

First, you need to use the event.target.files attribute of the FileEvent. This can be read using the common async JavaScript methods, like .text() or .arrayBuffer(). To read a CSV file from text with pandas you need to use an in-memory stream, like io.StringIO or io.BytesIO.

I adapted your PyScript code like this:

import pandas as pd
import pyodide
import io
import js

async def get_file(e):
    files = e.target.files.to_py()
    for file in files:
      file_content = await file.text()
      df = pd.read_csv(io.StringIO(file_content))
      js.console.log(df)

get_file_proxy = pyodide.ffi.create_proxy(get_file)

js.document.getElementById("fileinput").addEventListener("change", get_file_proxy)

Keep in mind, that large files might be better read using a buffer, but I do not exactly know best practices for this in PyScript.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your explanation. It works beautifully and is much better understandable than some other tutorials out there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.