From 1a46a763990cc14d19eb4cb56d69c59aa5b7fc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=AB=8B=E5=B8=AE?= <3294713004@qq.com> Date: Thu, 2 Oct 2025 10:05:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(boards):=20=E4=BF=AE=E5=A4=8Dmicropython=20?= =?UTF-8?q?TinyWebDB=E4=B8=8B=E8=AF=BB=E5=86=99=E4=B8=AD=E6=96=87=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../origin/build/lib/tiny_webdb.py | 10 +- .../origin/build/lib/urllib_parse.py | 187 ++++++++++++++++++ 2 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 boards/default_src/micropython/origin/build/lib/urllib_parse.py diff --git a/boards/default_src/micropython/origin/build/lib/tiny_webdb.py b/boards/default_src/micropython/origin/build/lib/tiny_webdb.py index 6d69d659..2c513fe4 100644 --- a/boards/default_src/micropython/origin/build/lib/tiny_webdb.py +++ b/boards/default_src/micropython/origin/build/lib/tiny_webdb.py @@ -1,4 +1,5 @@ import urequests as requests +import urllib_parse class TinyWebDB: @@ -22,14 +23,14 @@ class TinyWebDB: self._password = password def update(self, key, value): - key = str(key) - value = str(value) + key = urllib_parse.quote(str(key)) + value = urllib_parse.quote(str(value)) result = self._request("update", "tag={}&value={}".format(key, value)) if "status" in result and result["status"] == "error": raise RuntimeError(result["message"]) def get(self, key): - key = str(key) + key = urllib_parse.quote(str(key)) result = self._request("get", "tag={}".format(key)) if "status" in result and result["status"] == "error": raise RuntimeError(result["message"]) @@ -44,13 +45,14 @@ class TinyWebDB: def search(self, no=1, count=1, tag='', dtype='both'): no = str(no) count = str(count) + tag = urllib_parse.quote(tag) result = self._request("search", "no={}&count={}&tag={}&type={}".format(no, count, tag, dtype)) if "status" in result and result["status"] == "error": raise RuntimeError(result["message"]) return result["data"] def delete(self, key): - key = str(key) + key = urllib_parse.quote(str(key)) result = self._request("delete", "tag={}".format(key)) if "status" in result and result["status"] == "error": raise RuntimeError(result["message"]) diff --git a/boards/default_src/micropython/origin/build/lib/urllib_parse.py b/boards/default_src/micropython/origin/build/lib/urllib_parse.py new file mode 100644 index 00000000..b3ffaf0d --- /dev/null +++ b/boards/default_src/micropython/origin/build/lib/urllib_parse.py @@ -0,0 +1,187 @@ +# Characters valid in scheme names +scheme_chars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "+-." + +# XXX: Consider replacing with functools.lru_cache +MAX_CACHE_SIZE = 20 + +_ALWAYS_SAFE = frozenset(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ" b"abcdefghijklmnopqrstuvwxyz" b"0123456789" b"_.-") +_ALWAYS_SAFE_BYTES = bytes(_ALWAYS_SAFE) +_safe_quoters = {} + + +def clear_cache(): + """Clear the parse cache and the quoters cache.""" + _safe_quoters.clear() + + +_hexdig = "0123456789ABCDEFabcdef" +_hextobyte = {(a + b).encode(): bytes([int(a + b, 16)]) for a in _hexdig for b in _hexdig} + + +def unquote_to_bytes(string): + """unquote_to_bytes('abc%20def') -> b'abc def'.""" + # Note: strings are encoded as UTF-8. This is only an issue if it contains + # unescaped non-ASCII characters, which URIs should not. + if not string: + # Is it a string-like object? + string.split + return b"" + if isinstance(string, str): + string = string.encode("utf-8") + bits = string.split(b"%") + if len(bits) == 1: + return string + res = [bits[0]] + append = res.append + for item in bits[1:]: + try: + append(_hextobyte[item[:2]]) + append(item[2:]) + except KeyError: + append(b"%") + append(item) + return b"".join(res) + + +def split_on_non_ascii(s): + """ + Splits the input string wherever a character is not ASCII (ord(c) not in 0..127). + Returns a list of substrings and the non-ASCII characters as separate elements. + """ + result = [] + current = [] + for c in s: + if 0 <= ord(c) <= 127: + current.append(c) + else: + if current: + result.append("".join(current)) + current = [] + result.append(c) + if current: + result.append("".join(current)) + return result + + +def unquote(string, encoding="utf-8", errors="replace"): + """Replace %xx escapes by their single-character equivalent. The optional + encoding and errors parameters specify how to decode percent-encoded + sequences into Unicode characters, as accepted by the bytes.decode() + method. + By default, percent-encoded sequences are decoded with UTF-8, and invalid + sequences are replaced by a placeholder character. + + unquote('abc%20def') -> 'abc def'. + """ + if "%" not in string: + string.split + return string + if encoding is None: + encoding = "utf-8" + if errors is None: + errors = "replace" + bits = split_on_non_ascii(string) + res = [] + append = res.append + for i in range(0, len(bits), 2): + append(unquote_to_bytes(bits[i]).decode(encoding, errors)) + if i + 1 < len(bits): + # Append the non-ASCII part as is + append(bits[i + 1]) + return "".join(res) + + +class Quoter: + """A mapping from bytes (in range(0,256)) to strings. + + String values are percent-encoded byte values, unless the key < 128, and + in the "safe" set (either the specified safe set, or default set). + """ + + # Keeps a cache internally, using defaultdict, for efficiency (lookups + # of cached keys don't call Python code at all). + def __init__(self, safe): + """safe: bytes object.""" + self.safe = _ALWAYS_SAFE.union(safe) + self.cache = {} + + def get(self, b): + try: + return self.cache[b] + except KeyError: + # Handle a cache miss. Store quoted string in cache and return. + res = chr(b) if b in self.safe else "%{:02X}".format(b) + self.cache[b] = res + return res + + +def quote(string, safe="/", encoding=None, errors=None): + """quote('abc def') -> 'abc%20def' + + Each part of a URL, e.g. the path info, the query, etc., has a + different set of reserved characters that must be quoted. + + RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists + the following reserved characters. + + reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | + "$" | "," + + Each of these characters is reserved in some component of a URL, + but not necessarily in all of them. + + By default, the quote function is intended for quoting the path + section of a URL. Thus, it will not encode '/'. This character + is reserved, but in typical usage the quote function is being + called on a path where the existing slash characters are used as + reserved characters. + + string and safe may be either str or bytes objects. encoding must + not be specified if string is a str. + + The optional encoding and errors parameters specify how to deal with + non-ASCII characters, as accepted by the str.encode method. + By default, encoding='utf-8' (characters are encoded with UTF-8), and + errors='strict' (unsupported characters raise a UnicodeEncodeError). + """ + if isinstance(string, str): + if not string: + return string + if encoding is None: + encoding = "utf-8" + if errors is None: + errors = "strict" + string = string.encode(encoding, errors) + else: + if encoding is not None: + raise TypeError("quote() doesn't support 'encoding' for bytes") + if errors is not None: + raise TypeError("quote() doesn't support 'errors' for bytes") + return quote_from_bytes(string, safe) + + +def quote_from_bytes(bs, safe="/"): + """Like quote(), but accepts a bytes object rather than a str, and does + not perform string-to-bytes encoding. It always returns an ASCII string. + quote_from_bytes(b'abc def\x3f') -> 'abc%20def%3f' + """ + if not isinstance(bs, (bytes, bytearray)): + raise TypeError("quote_from_bytes() expected bytes") + if not bs: + return "" + if isinstance(safe, str): + # Normalize 'safe' by converting to bytes and removing non-ASCII chars + safe = safe.encode("ascii", "ignore") + else: + safe = bytes([c for c in safe if c < 128]) + if not bs.rstrip(_ALWAYS_SAFE_BYTES + safe): + return bs.decode() + try: + quoter = _safe_quoters[safe] + except KeyError as e: + _safe_quoters[safe] = quoter = Quoter(safe) + + res = "" + for char in bs: + res += quoter.get(char) + return res \ No newline at end of file