File cgi.py of Package failed_python-web.py

"""
Minimal compatibility shim for the removed `cgi` stdlib module (Python 3.13+).
This provides a very small subset of the original `cgi.FieldStorage` behaviour
that is sufficient for simple application/x-www-form-urlencoded POST parsing
used by the web.py package during build.

It intentionally implements only:
 - FieldStorage.__init__(fp=None, environ=None, keep_blank_values=False)
 - FieldStorage.getfirst(name, default=None)
 - FieldStorage.getvalue(name, default=None)
 - FieldStorage.__getitem__(name)

This avoids requiring changes to the upstream web.py sources while allowing
the package to build on Python versions that do not include the stdlib cgi.
"""

from urllib.parse import parse_qs
from io import BytesIO

class FieldStorage:
    """
    Lightweight FieldStorage replacement.
    - If environ is provided and CONTENT_LENGTH > 0, it will read that many bytes
      from fp (if fp provided) and parse them with urllib.parse.parse_qs.
    - Parsed values are stored as lists (to mimic parse_qs) and getfirst/getvalue
      return the first value or a provided default.
    Note: This implementation only supports application/x-www-form-urlencoded
    request bodies (typical for simple HTML forms). Multipart/form-data (file
    uploads) is not supported by this shim.
    """
    def __init__(self, fp=None, environ=None, keep_blank_values=False):
        self._data = {}
        self.list = []  # mimic real FieldStorage's .list to some degree

        if environ is None:
            return

        try:
            length = int(environ.get('CONTENT_LENGTH') or 0)
        except Exception:
            length = 0

        raw = b''
        if fp is not None and length > 0:
            try:
                # If fp is a text stream, attempt to read text and convert to bytes
                raw = fp.read(length)
            except TypeError:
                # some streams expect no length; read all
                try:
                    raw = fp.read()
                except Exception:
                    raw = b''

        # If we got text instead of bytes, convert to bytes
        if isinstance(raw, str):
            raw = raw.encode('utf-8', errors='replace')

        # decode using charset if provided, otherwise utf-8 with replace
        charset = 'utf-8'
        content_type = environ.get('CONTENT_TYPE', '') or ''
        if 'charset=' in content_type:
            try:
                charset = content_type.split('charset=')[-1].split(';', 1)[0].strip()
            except Exception:
                charset = 'utf-8'

        try:
            qs = raw.decode(charset, errors='replace')
        except Exception:
            qs = ''

        if qs:
            parsed = parse_qs(qs, keep_blank_values=keep_blank_values)
            # store as lists to be similar to cgi.FieldStorage behavior
            for k, v in parsed.items():
                self._data[k] = v
                self.list.append((k, v))

    def getfirst(self, name, default=None):
        v = self._data.get(name)
        if v:
            return v[0]
        return default

    def getvalue(self, name, default=None):
        return self.getfirst(name, default)

    def __getitem__(self, name):
        v = self.getfirst(name)
        if v is None:
            raise KeyError(name)
        return v

# Provide small helpers that some code might expect
def parse_qs(qs, keep_blank_values=False):
    return parse_qs(qs, keep_blank_values=keep_blank_values)  # type: ignore

# Keep module-level names that old code might import from cgi
FieldStorage = FieldStorage
openSUSE Build Service is sponsored by