Client code: ModuleNotFoundError: No module named 'urllib.parse'

Dear Friends:

The below code doesn’t work in Client code, which is where I’d like it to run, due to an import error. (Note: Imports are inside the function because Anvil complains about “not able to call a function here” when performed at the top of the file).

Error:

ModuleNotFoundError: No module named 'urllib.parse'

Code:

def get_short_url(long_url, *args, **kwargs):
    API_URL = "http://tinyurl.com/api-create.php" # The URL shortner API: TinyURL
    try:
       from urllib.request import urlopen
       from urllib.parse import urlencode  # <-- Offending statement.
       url = f"""{API_URL}?{urlencode({"url":long_url})}"""
       with urlopen(url) as response:
           short_url = response.read().decode('utf-8')
    except Exception as e:
       short_url = None
    return short_url

The code works outside of Anvil.

Does anyone know what I can substitute for urllib above? (urllib3 is only available for Server Modules). Or perhaps has Client-working URL shortener code that uses a free shortener service (like TinyURL).

Thank you!

put this in client code:

from ._anvil_designer import Form1Template
from anvil import *
import anvil.server

class Form1(Form1Template):

  def __init__(self, **properties):
    self.init_components(**properties)
    print(anvil.server.call('get_short_url','https://com-pute.com'))

and this in server code:

from ._anvil_designer import Form1Template
from anvil import *
import anvil.server

class Form1(Form1Template):

  def __init__(self, **properties):
    self.init_components(**properties)
    print(anvil.server.call('get_short_url','https://com-pute.com'))

https://anvil.works/build#clone:AG33DCDZLLS32QUH=N2RP6OHOK7ZCFFOQMJWWBIE3

Thank you. I think you pasted the same code twice. Also, I prefer not to make a backend call for this. I want to do it in Client code.

Lacking any suggestions on a prebuilt way of doing this on the client, you might look at the source for urlencode and see if you can port it to the client. It might work as is, or it might rely on features not yet in Skulpt: cpython/parse.py at main · python/cpython · GitHub

1 Like

This should be in the server:

import anvil.server
from urllib.request import urlopen
from urllib.parse import urlencode

@anvil.server.callable
def get_short_url(long_url, *args, **kwargs):
  API_URL = "http://tinyurl.com/api-create.php"
  try:
      url = f"""{API_URL}?{urlencode({"url":long_url})}"""
      with urlopen(url) as response:
          short_url = response.read().decode('utf-8')
  except Exception as e:
      short_url = None
  return short_url
1 Like

Thank you. Yes, if nothing easy works on the client, I’ll move my code to the server and perform a call_s().

You could look around for some existing client JS that interacts directly with tinyurl. Maybe try:

1 Like

Thank you. I’ll poke around the code.

Can you use Anvil’s HTTP requests to make the request, allowing it to handle url encoding? Anvil Docs | Making HTTP requests

Something like (totally untested):

        resp = anvil.http.request(url="http://tinyurl.com/api-create.php",
                    method="POST",
                    data={
                        "url": self.text_box_1.text,
                        
                    })

I’m sure you’ll need more, such as an API key, and other elements in the body.

1 Like

This approach looks interesting. Not totally yet familiar with the Anvil API, I didn’t think of this. Let me look into this later and post back. Thank you!

Let me start my REPL with a Client Uplink key to tinker with this suggestion. :smiley:

Hi!

Are you able to successfully issue:

import anvil.http

in a REPL with a Client or Server Uplink code?

In either case I get:

ModuleNotFoundError: No module named 'anvil.http'

Thank you.

Might not be available there, since you can install any package you want. Best to create a simple app to test with.

1 Like

Anvil.http is a library that can be imported into either client or server Anvil code - which if I understand correctly, you’re hoping to use instead of code in an Anvil server module. So I’d expect you to run anvil.http methods in a function in your Anvil client code, which could be called from uplink code (not loaded into code running on your local machine connected by the uplink).

You’re likely going to have a problem with anvil.http methods in Anvil client modules (running cross domain requests). You won’t have those same problems when using anvil.http methods in Anvil server modules, but if you’ve got an uplink running, you could just run the code you first presented at the beginning of this thread, there… but that seems like an unnecessary kludge because that exact functionality can be achieved by just moving some code into an Anvil server module, and then your app doesn’t require an uplink machine. I think of uplink being most useful the other way around - using the uplink as a server to perform heavy lifting that’s difficult or impossible to achieve on Anvil servers (some library that can’t be loaded in your Anvil account, or controlling some local hardware, etc).

I don’t know if you got a chance to check out the clone I linked above - it’s fully functional, using the exact code that you presented (just it moved out of a client module, and into a server module). Either way, I’m hoping to understand where the benefit of calling anvil.http methods in client modules, from calls in uplink code. Why go to all the trouble of running code in Anvil client modules (which will get stuck executing cross domain requests), when you could just run your original code on the uplink machine - or even better, simply in an Anvil server module?

1 Like

Hi. :relaxed: No the Uplink was just to perform some quick ad-hoc iteration via a REPL, not for anything permanent. Thank you for the explanation. Yes I saw the code share. :slight_smile:

I see what you mean. I’ll forego the Client REPL and, as you say, trial things in a small App.

PS: For now I have the code I wrote working in a backend Server Module, but I’ll try to get something running on the frontend.

The CORS issues Nick mentioned are likely to continue to be an issue on the client side. Browsers are pretty particular about which domains you connect out to from a client web app.

1 Like
  def __init__(self, **properties):
    self.init_components(**properties)

  def button_1_click(self, **event_args):
    url=(
      "http://tinyurl.com/api-create.php?url=" + 
      anvil.http.url_encode(self.text_box_1.text)
    )
    print(url)
    resp=anvil.http.request(url)
    self.text_box_2.text=resp

If you run the code above and copy the printed ‘url’ value into you browser, it returns a proper response (should also work in curl, etc.).

anvil.http.request(url) above returns 'HttpError: HTTP error 0". If anyone has some experience handling cross-origin requests as mentioned in part 3 of Anvil Docs | Making HTTP requests (‘It needs to respond to an OPTIONS request, have the right CORS headers, etc.’), I’d love to learn how that’s done.

1 Like

I’ll take a look at the JS library I mentioned - they do call the API from client code (perhaps it’s connecting to some backend code that I didn’t notice at first)…

1 Like