0

I've tried a number of things trying to import boto3 into a project I'm contributing to (thats built with pyodide)but keep receiving unhelpful errors. Is this a syntax issue or something more? This is the top half of index.html where I'm trying to import boto3 within py-env and py-script tags. Thanks so much for any guidance!

UPDATE: I moved away from boto3 due to deadends and now trying to call directly to s3 but I think the compiler is causing strange issues such as not throwing error messages and compiling commented lines of code. repo: https://github.com/stanleywalker1/capstone-studio-2

aws docs: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-photo-album.html

<html>
<head>
<title>Stablediffusion Infinity</title>
<meta charset="utf-8">
<link rel="icon" type="image/x-icon" href="./favicon.png">

<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/css/w2ui.min.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/js/w2ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/js/fabric.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/js/toolbar.js"></script>

<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>

<style>
#container {
  position: relative;
  margin:auto;
  display: block;
}
#container > canvas {
  position: absolute;
  top: 0;
  left: 0;
}
.control {
  display: none;
}
</style>

</head>
<body>
<div>
<button type="button" class="control" id="export">Export</button>
<button type="button" class="control" id="outpaint">Outpaint</button>
<button type="button" class="control" id="undo">Undo</button>
<button type="button" class="control" id="commit">Commit</button>
<button type="button" class="control" id="transfer">Transfer</button>
<button type="button" class="control" id="upload">Upload</button>
<button type="button" class="control" id="draw">Draw</button>
<input type="text" id="mode" value="selection" class="control">
<input type="text" id="setup" value="0" class="control">
<input type="text" id="upload_content" value="0" class="control">
<textarea rows="1" id="selbuffer" name="selbuffer" class="control"></textarea>
<fieldset class="control">
    <div>
      <input type="radio" id="mode0" name="mode" value="0" checked>
      <label for="mode0">SelBox</label>
    </div>
    <div>
      <input type="radio" id="mode1" name="mode" value="1">
      <label for="mode1">Image</label>
    </div>
    <div>
      <input type="radio" id="mode2" name="mode" value="2">
      <label for="mode2">Brush</label>
    </div>
</fieldset>
</div>
<div id = "outer_container">
<div id = "container">
  <canvas id = "canvas0"></canvas>
  <canvas id = "canvas1"></canvas>
  <canvas id = "canvas2"></canvas>
  <canvas id = "canvas3"></canvas>
  <canvas id = "canvas4"></canvas>
  <div id="overlay_container" style="pointer-events: none">
    <canvas id = "overlay_canvas" width="1" height="1"></canvas>
  </div>
</div>
<input type="file" name="file" id="upload_file" accept="image/*" hidden>
<input type="file" name="state" id="upload_state" accept=".sdinf" hidden>
<div style="position: relative;">
<div id="toolbar" style></div>
</div>
</div>
<py-env>
- numpy
- Pillow
- micropip:
  - boto3
- paths:
  - ./canvas.py
</py-env>

    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.864.0.min.js"></script>
    <script>
    alert("3");
    function aws(name, x, y){
        return `coming from javascript ${name} ${x} ${y}`;

    }

         function uploadfiletoaws(albumName) { 
            var albumBucketName = "nyucapstone-images";
                var bucketRegion = "us-east-2";
                var IdentityPoolId = "us-east-2:1913b386-4748-4dbd-b7b4-74f0bbb2e181";

                AWS.config.update({
                region: bucketRegion,
                credentials: new AWS.CognitoIdentityCredentials({
                    IdentityPoolId: IdentityPoolId
                })
                });

                var s3 = new AWS.S3({
                    apiVersion: '2006-03-01',
                    params: {Bucket: albumBucketName}
                });
             }

        
    </script>




    


<py-script>



from pyodide import to_js, create_proxy
from PIL import Image
import io
import time
import base64
from collections import deque
import numpy as np


from js import (
    console,
    document,
    parent,
    devicePixelRatio,
    ImageData,
    Uint8ClampedArray,
    CanvasRenderingContext2D as Context2d,
    requestAnimationFrame,
    window,
    encodeURIComponent,
    w2ui,
    update_eraser,
    update_scale,
    adjust_selection,
    update_count,
    enable_result_lst,
    setup_shortcut,
    update_undo_redo,
    alert,
    aws
)
#this is where the error is happening 


answer = aws("hello", 1, 2)
console.log(answer)

#addPhoto("demo")





from canvas import InfCanvas


class History:
    def __init__(self,maxlen=10):
        self.idx=-1
        self.undo_lst=deque([],maxlen=maxlen)
        self.redo_lst=deque([],maxlen=maxlen)
        self.state=None

    def undo(self):
        cur=None
        if len(self.undo_lst):
            cur=self.undo_lst.pop()
            self.redo_lst.appendleft(cur)
        return cur
    def redo(self):
        cur=None
        if len(self.redo_lst):
            cur=self.redo_lst.popleft()
            self.undo_lst.append(cur)
        return cur

    def check(self):
        return len(self.undo_lst)>0,len(self.redo_lst)>0

    def append(self,state,update=True):
        self.redo_lst.clear()
        self.undo_lst.append(state)
        if update:
            update_undo_redo(*self.check())

history = History()

base_lst = [None]
async def draw_canvas() -> None:
    width=1024
    height=600
    canvas=InfCanvas(1024,600)
    update_eraser(canvas.eraser_size,min(canvas.selection_size_h,canvas.selection_size_w))
    document.querySelector("#container").style.height= f"{height}px"
    document.querySelector("#container").style.width = f"{width}px"
    canvas.setup_mouse()
    canvas.clear_background()
    canvas.draw_buffer()
    canvas.draw_selection_box()
    base_lst[0]=canvas

async def draw_canvas_func(event):
    try:
        app=parent.document.querySelector("gradio-app")
        if app.shadowRoot:
            app=app.shadowRoot
        width=app.querySelector("#canvas_width input").value
        height=app.querySelector("#canvas_height input").value
        selection_size=app.querySelector("#selection_size input").value
    except:
        width=1024
        height=768
        selection_size=384
    document.querySelector("#container").style.width = f"{width}px"
    document.querySelector("#container").style.height= f"{height}px"
    canvas=InfCanvas(int(width),int(height),selection_size=int(selection_size))
    canvas.setup_mouse()
    canvas.clear_background()
    canvas.draw_buffer()
    canvas.draw_selection_box()
    base_lst[0]=canvas
  
async def export_func(event):
    base=base_lst[0]
    arr=base.export()
    base.draw_buffer()
    base.canvas[2].clear()
    base64_str = base.numpy_to_base64(arr)
    time_str = time.strftime("%Y%m%d_%H%M%S")
    link = document.createElement("a")
    if len(event.data)>2 and event.data[2]:
        filename = event.data[2]
    else:
        filename = f"outpaint_{time_str}"
    # link.download = f"sdinf_state_{time_str}.json"
    link.download = f"{filename}.png"
    # link.download = f"outpaint_{time_str}.png"
    link.href = "data:image/png;base64,"+base64_str
    link.click()




    console.log(f"Canvas saved to {filename}.png")

img_candidate_lst=[None,0]

async def outpaint_func(event):
    base=base_lst[0]
    if len(event.data)==2:
        app=parent.document.querySelector("gradio-app")
        if app.shadowRoot:
            app=app.shadowRoot
        base64_str_raw=app.querySelector("#output textarea").value
        base64_str_lst=base64_str_raw.split(",")
        img_candidate_lst[0]=base64_str_lst
        img_candidate_lst[1]=0
    elif event.data[2]=="next":
        img_candidate_lst[1]+=1
    elif event.data[2]=="prev":
        img_candidate_lst[1]-=1
    enable_result_lst()
    if img_candidate_lst[0] is None:
        return
    lst=img_candidate_lst[0]
    idx=img_candidate_lst[1]
    update_count(idx%len(lst)+1,len(lst))
    arr=base.base64_to_numpy(lst[idx%len(lst)])
    base.fill_selection(arr)
    base.draw_selection_box()

async def undo_func(event):
    base=base_lst[0]
    img_candidate_lst[0]=None
    if base.sel_dirty:
        base.sel_buffer = np.zeros((base.selection_size_h, base.selection_size_w, 4), dtype=np.uint8)
        base.sel_dirty = False
    base.canvas[2].clear()

async def commit_func(event):
    base=base_lst[0]
    img_candidate_lst[0]=None
    if base.sel_dirty:
        base.write_selection_to_buffer()
        base.draw_buffer()
    base.canvas[2].clear()
    if len(event.data)>2:
        history.append(base.save())

async def history_undo_func(event):
    base=base_lst[0]
    if base.buffer_dirty or len(history.redo_lst)>0:
        state=history.undo()
    else:
        history.undo()
        state=history.undo()
    if state is not None:
        base.load(state)
    update_undo_redo(*history.check())
    
async def history_setup_func(event):
    base=base_lst[0]
    history.undo_lst.clear()
    history.redo_lst.clear()
    history.append(base.save(),update=False)

async def history_redo_func(event):
    base=base_lst[0]
    if len(history.undo_lst)>0:
        state=history.redo()
    else:
        history.redo()
        state=history.redo()
    if state is not None:
        base.load(state)
    update_undo_redo(*history.check())


async def transfer_func(event):
    base=base_lst[0]
    base.read_selection_from_buffer()
    sel_buffer=base.sel_buffer
    sel_buffer_str=base.numpy_to_base64(sel_buffer)
    app=parent.document.querySelector("gradio-app")
    if app.shadowRoot:
        app=app.shadowRoot
    app.querySelector("#input textarea").value=sel_buffer_str
    app.querySelector("#proceed").click()

async def upload_func(event):
    base=base_lst[0]
    # base64_str=event.data[1]
    base64_str=document.querySelector("#upload_content").value
    base64_str=base64_str.split(",")[-1]
    # base64_str=parent.document.querySelector("gradio-app").shadowRoot.querySelector("#upload textarea").value
    arr=base.base64_to_numpy(base64_str)
    h,w,c=base.buffer.shape
    base.sync_to_buffer()
    base.buffer_dirty=True
    mask=arr[:,:,3:4].repeat(4,axis=2)
    base.buffer[mask>0]=0
    # in case mismatch
    base.buffer[0:h,0:w,:]+=arr
    #base.buffer[yo:yo+h,xo:xo+w,0:3]=arr[:,:,0:3]
    #base.buffer[yo:yo+h,xo:xo+w,-1]=arr[:,:,-1]
    base.draw_buffer()
    if len(event.data)>2:
        history.append(base.save())

async def setup_shortcut_func(event):
    setup_shortcut(event.data[1])
  

document.querySelector("#export").addEventListener("click",create_proxy(export_func))
document.querySelector("#undo").addEventListener("click",create_proxy(undo_func))
document.querySelector("#commit").addEventListener("click",create_proxy(commit_func))
document.querySelector("#outpaint").addEventListener("click",create_proxy(outpaint_func))
document.querySelector("#upload").addEventListener("click",create_proxy(upload_func))

document.querySelector("#transfer").addEventListener("click",create_proxy(transfer_func))
document.querySelector("#draw").addEventListener("click",create_proxy(draw_canvas_func))

async def setup_func():
    document.querySelector("#setup").value="1"

async def reset_func(event):
    base=base_lst[0]
    base.reset()
    
async def load_func(event):
    base=base_lst[0]
    base.load(event.data[1])

async def save_func(event):
    base=base_lst[0]
    json_str=base.save()
    time_str = time.strftime("%Y%m%d_%H%M%S")
    link = document.createElement("a")
    if len(event.data)>2 and event.data[2]:
        filename = str(event.data[2]).strip()
    else:
        filename = f"outpaint_{time_str}"
    # link.download = f"sdinf_state_{time_str}.json"
    link.download = f"{filename}.sdinf"
    link.href = "data:text/json;charset=utf-8,"+encodeURIComponent(json_str)
    link.click()

async def prev_result_func(event):
    base=base_lst[0]
    base.reset()

async def next_result_func(event):
    base=base_lst[0]
    base.reset()

async def zoom_in_func(event):
    base=base_lst[0]
    scale=base.scale
    if scale>=0.2:
        scale-=0.1
        if len(event.data)>2:
            base.update_scale(scale,int(event.data[2]),int(event.data[3]))
        else:
            base.update_scale(scale)
        scale=base.scale
        update_scale(f"{base.width}x{base.height} ({round(100/scale)}%)")

async def zoom_out_func(event):
    base=base_lst[0]
    scale=base.scale
    if scale<10:
        scale+=0.1
        console.log(len(event.data))
        if len(event.data)>2:
            base.update_scale(scale,int(event.data[2]),int(event.data[3]))
        else:
            base.update_scale(scale)
        scale=base.scale
        update_scale(f"{base.width}x{base.height} ({round(100/scale)}%)")

async def sync_func(event):
    base=base_lst[0]
    base.sync_to_buffer()
    base.canvas[2].clear()

async def eraser_size_func(event):
    base=base_lst[0]
    eraser_size=min(int(event.data[1]),min(base.selection_size_h,base.selection_size_w))
    eraser_size=max(8,eraser_size)
    base.eraser_size=eraser_size

async def resize_selection_func(event):
    base=base_lst[0]
    cursor=base.cursor
    if len(event.data)>3:
        console.log(event.data)
        base.cursor[0]=int(event.data[1])
        base.cursor[1]=int(event.data[2])
        base.selection_size_w=int(event.data[3])//8*8
        base.selection_size_h=int(event.data[4])//8*8
        base.refine_selection()
        base.draw_selection_box()
    elif len(event.data)>2:
        base.draw_selection_box()
    else:
        base.canvas[-1].clear()
        adjust_selection(cursor[0],cursor[1],base.selection_size_w,base.selection_size_h)

async def eraser_func(event):
    base=base_lst[0]
    if event.data[1]!="eraser":
        base.canvas[-2].clear()
    else:
        x,y=base.mouse_pos
        base.draw_eraser(x,y)

async def resize_func(event):
    base=base_lst[0]
    width=int(event.data[1])
    height=int(event.data[2])
    if width>=256 and height>=256:
        if max(base.selection_size_h,base.selection_size_w)>min(width,height):
            base.selection_size_h=256
            base.selection_size_w=256
        base.resize(width,height)

async def message_func(event):
    if event.data[0]=="click":
        if event.data[1]=="clear":
            await reset_func(event)
        elif event.data[1]=="save":
            await save_func(event)
        elif event.data[1]=="export":
            await export_func(event)
        elif event.data[1]=="accept":
            await commit_func(event)
        elif event.data[1]=="cancel":
            await undo_func(event)
        elif event.data[1]=="zoom_in":
            await zoom_in_func(event)
        elif event.data[1]=="zoom_out":
            await zoom_out_func(event)
        elif event.data[1]=="redo":
            await history_redo_func(event)
        elif event.data[1]=="undo":
            await history_undo_func(event)
        elif event.data[1]=="history":
            await history_setup_func(event)
    elif event.data[0]=="sync":
        await sync_func(event)
    elif event.data[0]=="load":
        await load_func(event)
    elif event.data[0]=="upload":
        await upload_func(event)
    elif event.data[0]=="outpaint":
        await outpaint_func(event)
    elif event.data[0]=="mode":
        if event.data[1]!="selection":
            await sync_func(event)
        await eraser_func(event)
        document.querySelector("#mode").value=event.data[1]
    elif event.data[0]=="transfer":
        await transfer_func(event)
    elif event.data[0]=="setup":
        await draw_canvas_func(event)
    elif event.data[0]=="eraser_size":
        await eraser_size_func(event)
    elif event.data[0]=="resize_selection":
        await resize_selection_func(event)
    elif event.data[0]=="shortcut":
        await setup_shortcut_func(event)
    elif event.data[0]=="resize":
        await resize_func(event)
    
window.addEventListener("message",create_proxy(message_func))

import asyncio

_ = await asyncio.gather(
  setup_func()
)
</py-script>

</body>
</html>

Stan
  • 1
  • 1
  • 4
    `const` is not a keyword, it's treated as an ordinary name, and Python doesn't understand another name `micropip` that just followed `const`. You are writing Python code, assignment in Python is simply `micropip = pyodide.pyimport("micropip")`, semi-colons following statements are not needed. – metatoaster Apr 13 '23 at 23:31
  • I see, Im not too familiar with python but trying to make it work. I made the changes as you mentioned but still get this error message: `await micropip.install( ^ SyntaxError: '(' was never closed` This is what it looks like: `import micropip await pyodide.loadPackage("micropip") micropip = pyodide.pyimport("micropip") await micropip.install('boto3') import boto3` – Stan Apr 14 '23 at 02:31
  • You should update your question, there are no line breaks in comments, and the error is exactly what it is, put the closing parentheses at the correct location. Perhaps you should follow a tutorial first before stumbling around blindly trying to make things work. – metatoaster Apr 14 '23 at 05:50
  • sorry I'm working through tutorials but still struggling. I updated the question above. thanks for your guidance I'll keep trying to figure it out. this is what I'm trying to follow: https://pyodide.org/en/stable/usage/loading-packages.html – Stan Apr 14 '23 at 18:05

1 Answers1

0

I ran your code, and I could not find your error. Perhaps that error does not appear anymore after your edits.

There is an error in the code, in the sense that the html is not complete, there are tags that are not closed. Of course, the browser does not care, but you should add them for clarity, perhaps.

Anyway, the current error for me is that it does not find the canvas.py file in my path, which is normal, because it's not there for me. Can you please update the canvas.py code or remove it from the question, in a way in which the errors persist? I would like to help you further, by editing this answer, if you do this.

There is no current indication that boto3 is not installed in this process, though.

Edit:

Ok, so I actually had the time to look into it. The code that I ended up with is this:

<html>
<head>
<meta charset="utf-8">
<link rel="icon" type="image/x-icon" href="./favicon.png">

<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/css/w2ui.min.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/js/w2ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/js/fabric.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/gh/lkwq007/stablediffusion-infinity@v0.1.2/js/toolbar.js"></script>

<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/latest/pyscript.js"></script>

</head>
<body>

<py-config type="json">
  {
    "packages": ["Pillow", "numpy", "boto3"]
  }
</py-config>

<py-script>


from pyodide import to_js, create_proxy
from PIL import Image
import io
import time
import base64
from collections import deque
import numpy as np

import boto3


from js import (
    console,
    document,
    parent,
    devicePixelRatio,
    ImageData,
    Uint8ClampedArray,
    CanvasRenderingContext2D as Context2d,
    requestAnimationFrame,
    window,
    encodeURIComponent,
    w2ui,
)
</py-script>
</body>
</html>

There are some differences: the pyscript url is changed for the latest and I used <py-config> for packages.

boto3 seems to be downloaded correctly, but it errs again, because of the ssl module.

...
  File "/lib/python3.10/site-packages/botocore/httpsession.py", line 22, in <module>
    from urllib3.util.ssl_ import (
ImportError: cannot import name 'ssl' from 'urllib3.util.ssl_' (/lib/python3.10/site-packages/urllib3/util/ssl_.py)

/lib/python3.10/pyodide/__init__.py:74: FutureWarning: pyodide.to_js has been moved to pyodide.ffi.to_js Accessing it through the pyodide module is deprecated.
  warn(
/lib/python3.10/pyodide/__init__.py:74: FutureWarning: pyodide.create_proxy has been moved to pyodide.ffi.create_proxy Accessing it through the pyodide module is deprecated.
  warn(

The story of ssl on pyscript is uncertain, because of browser sandboxing. Basically, there are no APIs capable of doing these low-levels operations in the browser.

It seems that if you get urllib3 working correctly, you are close to a solution, but in your case, I would just work around boto3, as it does not do much, maybe you can get the s3 to work using pure fetch calls.

TachyonicBytes
  • 700
  • 1
  • 8
  • This is so helpful thank you for taking the time to look into this. I also pushed my changes to my repo here: https://github.com/stanleywalker1/capstone-studio-2 I'm going to come back to your notes tomorrow and see what I can do. I will update soon! – Stan Apr 17 '23 at 04:00
  • Hello! I took your advice and trying to call direct to s3. I updated the file above and also pushed to my git repo. Still no luck and it is not providing error messages. – Stan Apr 17 '23 at 21:55
  • I'll take a look later in the day, now that I also have the repo, it should work faster. – TachyonicBytes Apr 18 '23 at 10:40
  • Please let me know if you have any ideas. I've gave up on AWS, trying to integrate firebase now but still struggling with the same issue - due to the pyodide environment I believe. – Stan Apr 20 '23 at 20:05
  • Yeah, basically anything that talks to external resources is best done in javascript for now. I want to take another crack at it, but I will have some free time only tomorrow. – TachyonicBytes Apr 20 '23 at 20:07
  • I created a separate project for testing. I built an image gallery that sends and retrieves images from my firebase storage. The firebase configuration in here is most up to date and working to reference, i need to update main project to reflect this still. https://github.com/stanleywalker1/firebase-image-gallery-framework – Stan Apr 21 '23 at 03:11
  • I think I might have it working however not able to retrieve the image yet, just pushed newest to git repo. – Stan Apr 21 '23 at 07:18