Skip to content

Commit

Permalink
Fix micropython issue where urls were not being parsed
Browse files Browse the repository at this point in the history
  • Loading branch information
kkinder committed Jun 16, 2024
1 parent 9bf7c60 commit 39e0eb5
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
2 changes: 2 additions & 0 deletions examples/tutorial/10_full_app/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ def populate(self):
t.sl_button("Login", type="submit")
t.p("Use any username or password")

t.p(f"You will be returned to {self.return_to}")

def on_submit(self, event):
event.preventDefault()
if self.refs["form"].element.checkValidity():
Expand Down
58 changes: 56 additions & 2 deletions puepy/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,57 @@
from .util import mixed_to_underscores, jsobj


def _micropython_parse_query_string(query_string):
"""
In MicroPython, urllib isn't available and we can't use the JavaScript library:
https://github.com/pyscript/pyscript/issues/2100
"""
if query_string and query_string[0] == "?":
query_string = query_string[1:]

def url_decode(s):
# Decode URL-encoded characters without using regex, which is also pretty broken in MicroPython...
i = 0
length = len(s)
decoded = []

while i < length:
if s[i] == "%":
if i + 2 < length:
hex_value = s[i + 1 : i + 3]
decoded.append(chr(int(hex_value, 16)))
i += 3
else:
decoded.append("%")
i += 1
elif s[i] == "+":
decoded.append(" ")
i += 1
else:
decoded.append(s[i])
i += 1

return "".join(decoded)

params = {}
for part in query_string.split("&"):
if "=" in part:
key, value = part.split("=", 1)
key = url_decode(key)
value = url_decode(value)
if key in params:
params[key].append(value)
else:
params[key] = [value]
else:
key = url_decode(part)
if key in params:
params[key].append("")
else:
params[key] = ""
return params


if platform == PLATFORM_MICROPYTHON:
from js import encodeURIComponent

Expand All @@ -18,6 +69,9 @@ def url_quote(s):
def parse_query_string(qs):
return parse_qs(urlparse(qs).query)

elif platform == PLATFORM_MICROPYTHON:
parse_query_string = _micropython_parse_query_string

else:
import js

Expand Down Expand Up @@ -142,8 +196,8 @@ def match(self, path):
def navigate_to_path(self, path, **kwargs):
if isinstance(path, type) and issubclass(path, Page):
path = self.reverse(path, **kwargs)
else:
path = path + "?" + "&".join(f"{url_quote(k)}={url_quote(v)}" for k, v in kwargs.items())
elif kwargs:
path += "?" + "&".join(f"{url_quote(k)}={url_quote(v)}" for k, v in kwargs.items())

if self.link_mode == self.LINK_MODE_DIRECT:
window.location = path
Expand Down
47 changes: 46 additions & 1 deletion tests/test_router.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import unittest

from puepy.core import Page
from puepy.router import Router, Route
from puepy.router import Router, Route, _micropython_parse_query_string


class TestRoute(unittest.TestCase):
Expand Down Expand Up @@ -90,5 +90,50 @@ def test_no_match_route(self):
self.assertIsNone(params)


class TestMicropythonParseQueryString(unittest.TestCase):
def test_simple_query(self):
query_string = "?name=John"
expected_output = {"name": ["John"]}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)

def test_multiple_params(self):
query_string = "?name=John&age=30"
expected_output = {"name": ["John"], "age": ["30"]}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)

def test_url_encoded_chars(self):
query_string = "?name=John%20Doe&age=30"
expected_output = {"name": ["John Doe"], "age": ["30"]}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)

def test_repeated_params(self):
query_string = "?name=John&name=Jane"
expected_output = {"name": ["John", "Jane"]}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)

def test_no_value_param(self):
query_string = "?name="
expected_output = {"name": [""]}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)

def test_no_value_multiple_params(self):
query_string = "?name=&age="
expected_output = {"name": [""], "age": [""]}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)

def test_plus_as_space(self):
query_string = "?name=John+Doe&age=30"
expected_output = {"name": ["John Doe"], "age": ["30"]}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)

def test_single_param_without_value(self):
query_string = "?name"
expected_output = {"name": ""}
self.assertEqual(_micropython_parse_query_string(query_string), expected_output)


if __name__ == "__main__":
unittest.main()

if __name__ == "__main__":
unittest.main()

0 comments on commit 39e0eb5

Please sign in to comment.