跳转到内容

模組:沙盒/a2569875/PythonScript/Librarys

维基百科,自由的百科全书
-- Module:PythonScript/Librarys
-- 中小型Built-in標準庫合集(二)
-- 1. itertools
-- 2. functools
-- 3. collections
local Librarys = {}
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")

-- ==========================================
-- 內部輔助函數
-- ==========================================
local function wrap_func(func)
    return function(py_args, py_kwargs)
        if py_kwargs and next(py_kwargs) then
            Runtime.raise("TypeError", "module functions do not support keyword arguments")
        end
        return func(unpack(py_args or {}))
    end
end

local function make_namespace(name, tbl)
    local dict = {}
    for k, v in pairs(tbl) do
        if type(v) == "function" then
            dict[k] = wrap_func(v)
        elseif type(v) == "table" then
            dict[k] = make_namespace(name .. "_" .. k, v)
        else
            dict[k] = v
        end
    end
    return PyClass.make_instance(PyClass.make_class(name, {}, dict))
end

local stdlibs = {}

-- ==========================================
-- 1. itertools
-- ==========================================
local function init_itertools_module()
    local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
    local B = Runtime.builtins
    local SENTINEL = {}
    local cache = {}
    local iter_classes = {}

    local function is_none(v)
        return v == nil or (type(v) == "table" and v.__class and v.__class.__name == "NoneType")
    end

    local function to_int(v, err_msg)
        if type(v) == "boolean" then return v and 1 or 0 end
        if type(v) == "table" and v.__type == "int" then
            local MathCore = require("Module:沙盒/a2569875/PythonScript/Runtime/Cmath")
            v = v.value or tonumber(MathCore.BigInt.to_string(v.sign, v.limbs))
        end
        if type(v) ~= "number" or math.floor(v) ~= v then
            Runtime.raise("TypeError", err_msg or "expected integer")
        end
        return v
    end

    local function to_array(iterable)
        if type(iterable) == "table" and (iterable.__type == "list" or iterable.__type == "tuple") then
            local copy = {}
            for i = 1, #iterable.elements do copy[i] = iterable.elements[i] end
            return copy
        end
        local arr = {}
        local it = B.iter({iterable})
        while true do
            local v = B.next({it, SENTINEL})
            if v == SENTINEL then break end
            table.insert(arr, v)
        end
        return arr
    end

    local function make_iter(name, step_fn)
        local cls = iter_classes[name]
        if not cls then
            cls = PyClass.make_class(name, {}, {
                __iter__ = { __type = "builtin_method", __call = function(a) return a[1] end },
                __next__ = { __type = "builtin_method", __call = function(a) return a[1].__dict._step() end }
            })
            cls.__dict["__module__"] = "itertools"
            iter_classes[name] = cls
        end
        local inst = PyClass.make_instance(cls)
        inst.__dict._step = step_fn
        return inst
    end

    local function make_chain_from_outer(outer_it)
        local inner_it = nil
        return make_iter("chain", function()
            while true do
                if not inner_it then
                    local next_iterable = B.next({outer_it, SENTINEL})
                    if next_iterable == SENTINEL then Runtime.raise("StopIteration") end
                    inner_it = B.iter({next_iterable})
                end
                local val = B.next({inner_it, SENTINEL})
                if val ~= SENTINEL then return val end
                inner_it = nil
            end
        end)
    end

    local function get_attr(attr)
        if cache[attr] ~= nil then return cache[attr] end

        -- [1] 無限迭代器家族: count, cycle, repeat
        if attr == "count" then
            cache.count = {
                __type = "builtin_method", __name = "count",
                __call = function(args, kwargs)
                    local start = (args and args[1] ~= nil) and args[1] or (kwargs and kwargs.start) or 0
                    local step = (args and args[2] ~= nil) and args[2] or (kwargs and kwargs.step) or 1
                    local curr = start
                    return make_iter("count", function()
                        local ret = curr
                        if type(curr) == "number" and type(step) == "number" then
                            local nxt = curr + step
                            if math.abs(nxt) > 9007199254740990 and math.floor(curr) == curr and math.floor(step) == step then
                                curr = Evaluator.bigint_op(curr, step, "+")
                            else
                                curr = nxt
                            end
                        elseif (type(curr) == "table" and curr.__type == "int") or (type(step) == "table" and step.__type == "int") then
                            curr = Evaluator.bigint_op(curr, step, "+")
                        elseif (type(curr) == "table" and curr.__type == "complex") or (type(step) == "table" and step.__type == "complex") then
                            curr = require("Module:沙盒/a2569875/PythonScript/Runtime/Cmath").Cmath.add(curr, step)
                        else
                            Runtime.raise("TypeError", "a number is required")
                        end
                        return ret
                    end)
                end
            }
            return cache.count

        elseif attr == "cycle" then
            cache.cycle = {
                __type = "builtin_method", __name = "cycle",
                __call = function(args)
                    if not args or #args ~= 1 then Runtime.raise("TypeError", "cycle expected 1 argument") end
                    local it = B.iter({args[1]})
                    local saved = {}
                    local exhausted = false
                    local idx = 0
                    return make_iter("cycle", function()
                        if not exhausted then
                            local v = B.next({it, SENTINEL})
                            if v ~= SENTINEL then
                                table.insert(saved, v)
                                return v
                            end
                            exhausted = true
                        end
                        if #saved == 0 then Runtime.raise("StopIteration") end
                        idx = (idx % #saved) + 1
                        return saved[idx]
                    end)
                end
            }
            return cache.cycle

        elseif attr == "repeat" then
            cache["repeat"] = {
                __type = "builtin_method", __name = "repeat",
                __call = function(args, kwargs)
                    if not args or #args < 1 then Runtime.raise("TypeError", "repeat() missing required argument 'object'") end
                    local obj = args[1]
                    local raw_times = (args[2] ~= nil) and args[2] or (kwargs and kwargs.times)
                    local has_times = not is_none(raw_times)
                    local remaining = has_times and math.max(0, to_int(raw_times)) or nil
                    return make_iter("repeat", function()
                        if has_times then
                            if remaining <= 0 then Runtime.raise("StopIteration") end
                            remaining = remaining - 1
                        end
                        return obj
                    end)
                end
            }
            return cache["repeat"]

        -- [2] 串接與切片: chain (含 from_iterable), islice
        elseif attr == "chain" then
            local chain_fn = {
                __type = "builtin_method", __name = "chain",
                __dict = {
                    from_iterable = {
                        __type = "builtin_method", __name = "from_iterable",
                        __call = function(args)
                            if not args or #args ~= 1 then Runtime.raise("TypeError", "from_iterable() takes exactly one argument") end
                            return make_chain_from_outer(B.iter({args[1]}))
                        end
                    }
                },
                __call = function(args)
                    local list_arg = { __type = "list", elements = args or {} }
                    return make_chain_from_outer(B.iter({list_arg}))
                end
            }
            cache.chain = chain_fn
            return cache.chain

        elseif attr == "islice" then
            cache.islice = {
                __type = "builtin_method", __name = "islice",
                __call = function(args)
                    if not args or #args < 2 or #args > 4 then
                        Runtime.raise("TypeError", "islice expected 2 to 4 arguments")
                    end
                    local it = B.iter({args[1]})
                    local start, stop, step = 0, nil, 1
                    if #args == 2 then
                        if not is_none(args[2]) then stop = to_int(args[2]) end
                    else
                        if not is_none(args[2]) then start = to_int(args[2]) end
                        if not is_none(args[3]) then stop = to_int(args[3]) end
                        if #args == 4 and not is_none(args[4]) then step = to_int(args[4]) end
                    end
                    if start < 0 or (stop ~= nil and stop < 0) or step < 1 then
                        Runtime.raise("ValueError", "Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize, and step >= 1.")
                    end
                    local curr_idx = 0
                    local next_idx = start
                    local done = false
                    return make_iter("islice", function()
                        if done or (stop ~= nil and next_idx >= stop) then
                            done = true
                            Runtime.raise("StopIteration")
                        end
                        while curr_idx < next_idx do
                            local skipped = B.next({it, SENTINEL})
                            if skipped == SENTINEL then
                                done = true
                                Runtime.raise("StopIteration")
                            end
                            curr_idx = curr_idx + 1
                        end
                        local val = B.next({it, SENTINEL})
                        if val == SENTINEL then
                            done = true
                            Runtime.raise("StopIteration")
                        end
                        curr_idx = curr_idx + 1
                        next_idx = next_idx + step
                        return val
                    end)
                end
            }
            return cache.islice

        -- [3] 資料轉換與配對: accumulate, compress, starmap, pairwise, batched, zip_longest, dropwhile, takewhile, filterfalse
        elseif attr == "accumulate" then
            cache.accumulate = {
                __type = "builtin_method", __name = "accumulate",
                __call = function(args, kwargs)
                    if not args or #args < 1 then Runtime.raise("TypeError", "accumulate() missing required argument 'iterable'") end
                    local it = B.iter({args[1]})
                    local func = (args[2] ~= nil) and args[2] or (kwargs and kwargs.func)
                    if is_none(func) then func = nil end
                    local initial = kwargs and kwargs.initial
                    local has_initial = not is_none(initial)
                    local started = false
                    local total = nil

                    local function default_add(a, b)
                        if type(a) == "number" and type(b) == "number" then
                            local s = a + b
                            if math.abs(s) > 9007199254740990 and math.floor(a) == a and math.floor(b) == b then
                                return Evaluator.bigint_op(a, b, "+")
                            end
                            return s
                        elseif (type(a) == "table" and a.__type == "int") or (type(b) == "table" and b.__type == "int") then
                            return Evaluator.bigint_op(a, b, "+")
                        elseif type(a) == "string" and type(b) == "string" then
                            return a .. b
                        elseif type(a) == "table" and type(b) == "table" and a.__type == b.__type and (a.__type == "list" or a.__type == "tuple") then
                            local res = {}
                            for _, v in ipairs(a.elements) do table.insert(res, v) end
                            for _, v in ipairs(b.elements) do table.insert(res, v) end
                            return { __type = a.__type, elements = res }
                        end
                        Runtime.raise("TypeError", "unsupported operand type(s) for + in accumulate()")
                    end

                    return make_iter("accumulate", function()
                        if not started then
                            started = true
                            if has_initial then
                                total = initial
                                return total
                            end
                            local first = B.next({it, SENTINEL})
                            if first == SENTINEL then Runtime.raise("StopIteration") end
                            total = first
                            return total
                        end
                        local val = B.next({it, SENTINEL})
                        if val == SENTINEL then Runtime.raise("StopIteration") end
                        if func then
                            total = Evaluator.invoke(func, {total, val}, {})
                        else
                            total = default_add(total, val)
                        end
                        return total
                    end)
                end
            }
            return cache.accumulate

        elseif attr == "compress" then
            cache.compress = {
                __type = "builtin_method", __name = "compress",
                __call = function(args, kwargs)
                    local data = (args and args[1]) or (kwargs and kwargs.data)
                    local selectors = (args and args[2]) or (kwargs and kwargs.selectors)
                    if data == nil or selectors == nil then Runtime.raise("TypeError", "compress() requires data and selectors") end
                    local it_d = B.iter({data})
                    local it_s = B.iter({selectors})
                    return make_iter("compress", function()
                        while true do
                            local d = B.next({it_d, SENTINEL})
                            if d == SENTINEL then Runtime.raise("StopIteration") end
                            local s = B.next({it_s, SENTINEL})
                            if s == SENTINEL then Runtime.raise("StopIteration") end
                            if B.bool.__call({s}) then return d end
                        end
                    end)
                end
            }
            return cache.compress

        elseif attr == "starmap" then
            cache.starmap = {
                __type = "builtin_method", __name = "starmap",
                __call = function(args)
                    if not args or #args ~= 2 then Runtime.raise("TypeError", "starmap expected 2 arguments") end
                    local func = args[1]
                    local it = B.iter({args[2]})
                    return make_iter("starmap", function()
                        local seq = B.next({it, SENTINEL})
                        if seq == SENTINEL then Runtime.raise("StopIteration") end
                        return Evaluator.invoke(func, to_array(seq), {})
                    end)
                end
            }
            return cache.starmap

        elseif attr == "pairwise" then
            cache.pairwise = {
                __type = "builtin_method", __name = "pairwise",
                __call = function(args)
                    if not args or #args ~= 1 then Runtime.raise("TypeError", "pairwise expected 1 argument") end
                    local it = B.iter({args[1]})
                    local prev = nil
                    local initialized = false
                    local done = false
                    return make_iter("pairwise", function()
                        if done then Runtime.raise("StopIteration") end
                        if not initialized then
                            initialized = true
                            prev = B.next({it, SENTINEL})
                            if prev == SENTINEL then
                                done = true
                                Runtime.raise("StopIteration")
                            end
                        end
                        local curr = B.next({it, SENTINEL})
                        if curr == SENTINEL then
                            done = true
                            Runtime.raise("StopIteration")
                        end
                        local res = { __type = "tuple", elements = { prev, curr } }
                        prev = curr
                        return res
                    end)
                end
            }
            return cache.pairwise

        elseif attr == "batched" then
            cache.batched = {
                __type = "builtin_method", __name = "batched",
                __call = function(args)
                    if not args or #args ~= 2 then Runtime.raise("TypeError", "batched expected 2 arguments") end
                    local it = B.iter({args[1]})
                    local n = to_int(args[2])
                    if n < 1 then Runtime.raise("ValueError", "n must be at least one") end
                    local done = false
                    return make_iter("batched", function()
                        if done then Runtime.raise("StopIteration") end
                        local batch = {}
                        for i = 1, n do
                            local v = B.next({it, SENTINEL})
                            if v == SENTINEL then
                                done = true
                                break
                            end
                            table.insert(batch, v)
                        end
                        if #batch == 0 then Runtime.raise("StopIteration") end
                        return { __type = "tuple", elements = batch }
                    end)
                end
            }
            return cache.batched

        elseif attr == "zip_longest" then
            cache.zip_longest = {
                __type = "builtin_method", __name = "zip_longest",
                __call = function(args, kwargs)
                    local fillvalue = (kwargs and kwargs.fillvalue ~= nil) and kwargs.fillvalue or B.None
                    local iters = {}
                    for i, arg in ipairs(args or {}) do
                        iters[i] = B.iter({arg})
                    end
                    local active = #iters
                    return make_iter("zip_longest", function()
                        if active == 0 then Runtime.raise("StopIteration") end
                        local values = {}
                        for i = 1, #iters do
                            local it = iters[i]
                            if not it then
                                values[i] = fillvalue
                            else
                                local v = B.next({it, SENTINEL})
                                if v == SENTINEL then
                                    active = active - 1
                                    if active == 0 then Runtime.raise("StopIteration") end
                                    iters[i] = false
                                    values[i] = fillvalue
                                else
                                    values[i] = v
                                end
                            end
                        end
                        return { __type = "tuple", elements = values }
                    end)
                end
            }
            return cache.zip_longest

        elseif attr == "dropwhile" then
            cache.dropwhile = {
                __type = "builtin_method", __name = "dropwhile",
                __call = function(args)
                    if not args or #args ~= 2 then Runtime.raise("TypeError", "dropwhile expected 2 arguments") end
                    local pred, it = args[1], B.iter({args[2]})
                    local dropping = true
                    return make_iter("dropwhile", function()
                        while true do
                            local v = B.next({it, SENTINEL})
                            if v == SENTINEL then Runtime.raise("StopIteration") end
                            if dropping then
                                if not B.bool.__call({Evaluator.invoke(pred, {v}, {})}) then
                                    dropping = false
                                    return v
                                end
                            else
                                return v
                            end
                        end
                    end)
                end
            }
            return cache.dropwhile

        elseif attr == "takewhile" then
            cache.takewhile = {
                __type = "builtin_method", __name = "takewhile",
                __call = function(args)
                    if not args or #args ~= 2 then Runtime.raise("TypeError", "takewhile expected 2 arguments") end
                    local pred, it = args[1], B.iter({args[2]})
                    local done = false
                    return make_iter("takewhile", function()
                        if done then Runtime.raise("StopIteration") end
                        local v = B.next({it, SENTINEL})
                        if v == SENTINEL then done = true; Runtime.raise("StopIteration") end
                        if B.bool.__call({Evaluator.invoke(pred, {v}, {})}) then
                            return v
                        end
                        done = true
                        Runtime.raise("StopIteration")
                    end)
                end
            }
            return cache.takewhile

        elseif attr == "filterfalse" then
            cache.filterfalse = {
                __type = "builtin_method", __name = "filterfalse",
                __call = function(args)
                    if not args or #args ~= 2 then Runtime.raise("TypeError", "filterfalse expected 2 arguments") end
                    local pred, it = args[1], B.iter({args[2]})
                    local use_bool = is_none(pred)
                    return make_iter("filterfalse", function()
                        while true do
                            local v = B.next({it, SENTINEL})
                            if v == SENTINEL then Runtime.raise("StopIteration") end
                            local cond = use_bool and B.bool.__call({v}) or B.bool.__call({Evaluator.invoke(pred, {v}, {})})
                            if not cond then return v end
                        end
                    end)
                end
            }
            return cache.filterfalse

        -- [4] 排列組合與笛卡兒積家族: product, permutations, combinations, combinations_with_replacement
        elseif attr == "product" then
            cache.product = {
                __type = "builtin_method", __name = "product",
                __call = function(args, kwargs)
                    local rep = (kwargs and kwargs["repeat"] ~= nil) and to_int(kwargs["repeat"]) or 1
                    if rep < 0 then Runtime.raise("ValueError", "repeat argument cannot be negative") end
                    local base_pools = {}
                    for i, arg in ipairs(args or {}) do
                        base_pools[i] = to_array(arg)
                    end
                    local pools = {}
                    local done = false
                    for _ = 1, rep do
                        for _, p in ipairs(base_pools) do
                            if #p == 0 then done = true end
                            table.insert(pools, p)
                        end
                    end
                    local k = #pools
                    local indices = {}
                    for i = 1, k do indices[i] = 1 end
                    local first = true

                    return make_iter("product", function()
                        if done then Runtime.raise("StopIteration") end
                        if k == 0 then
                            done = true
                            return { __type = "tuple", elements = {} }
                        end
                        if first then
                            first = false
                        else
                            local pos = k
                            while pos >= 1 do
                                indices[pos] = indices[pos] + 1
                                if indices[pos] <= #pools[pos] then break end
                                indices[pos] = 1
                                pos = pos - 1
                            end
                            if pos == 0 then
                                done = true
                                Runtime.raise("StopIteration")
                            end
                        end
                        local res = {}
                        for i = 1, k do res[i] = pools[i][indices[i]] end
                        return { __type = "tuple", elements = res }
                    end)
                end
            }
            return cache.product

        elseif attr == "permutations" then
            cache.permutations = {
                __type = "builtin_method", __name = "permutations",
                __call = function(args, kwargs)
                    if not args or #args < 1 then Runtime.raise("TypeError", "permutations() missing required argument 'iterable'") end
                    local pool = to_array(args[1])
                    local n = #pool
                    local raw_r = (args[2] ~= nil) and args[2] or (kwargs and kwargs.r)
                    local r = is_none(raw_r) and n or to_int(raw_r)
                    if r < 0 then Runtime.raise("ValueError", "r must be non-negative") end

                    local done = (r > n)
                    local first = true
                    local indices, cycles = {}, {}
                    for i = 1, n do indices[i] = i end
                    for i = 1, r do cycles[i] = n - i + 1 end

                    return make_iter("permutations", function()
                        if done then Runtime.raise("StopIteration") end
                        if r == 0 then
                            done = true
                            return { __type = "tuple", elements = {} }
                        end
                        if first then
                            first = false
                            local res = {}
                            for i = 1, r do res[i] = pool[indices[i]] end
                            return { __type = "tuple", elements = res }
                        end
                        local advanced = false
                        for i = r, 1, -1 do
                            cycles[i] = cycles[i] - 1
                            if cycles[i] == 0 then
                                local tmp = indices[i]
                                for k = i, n - 1 do indices[k] = indices[k + 1] end
                                indices[n] = tmp
                                cycles[i] = n - i + 1
                            else
                                local j = n - cycles[i] + 1
                                indices[i], indices[j] = indices[j], indices[i]
                                advanced = true
                                break
                            end
                        end
                        if not advanced then
                            done = true
                            Runtime.raise("StopIteration")
                        end
                        local res = {}
                        for i = 1, r do res[i] = pool[indices[i]] end
                        return { __type = "tuple", elements = res }
                    end)
                end
            }
            return cache.permutations

        elseif attr == "combinations" then
            cache.combinations = {
                __type = "builtin_method", __name = "combinations",
                __call = function(args, kwargs)
                    if not args or #args < 1 then Runtime.raise("TypeError", "combinations() missing required argument") end
                    local pool = to_array(args[1])
                    local n = #pool
                    local raw_r = (args[2] ~= nil) and args[2] or (kwargs and kwargs.r)
                    if raw_r == nil then Runtime.raise("TypeError", "combinations() missing required argument 'r'") end
                    local r = to_int(raw_r)
                    if r < 0 then Runtime.raise("ValueError", "r must be non-negative") end

                    local done = (r > n)
                    local first = true
                    local indices = {}
                    for i = 1, r do indices[i] = i end

                    return make_iter("combinations", function()
                        if done then Runtime.raise("StopIteration") end
                        if r == 0 then
                            done = true
                            return { __type = "tuple", elements = {} }
                        end
                        if first then
                            first = false
                        else
                            local piv = 0
                            for i = r, 1, -1 do
                                if indices[i] ~= i + n - r then
                                    piv = i
                                    break
                                end
                            end
                            if piv == 0 then
                                done = true
                                Runtime.raise("StopIteration")
                            end
                            indices[piv] = indices[piv] + 1
                            for j = piv + 1, r do
                                indices[j] = indices[j - 1] + 1
                            end
                        end
                        local res = {}
                        for i = 1, r do res[i] = pool[indices[i]] end
                        return { __type = "tuple", elements = res }
                    end)
                end
            }
            return cache.combinations

        elseif attr == "combinations_with_replacement" then
            cache.combinations_with_replacement = {
                __type = "builtin_method", __name = "combinations_with_replacement",
                __call = function(args, kwargs)
                    if not args or #args < 1 then Runtime.raise("TypeError", "combinations_with_replacement() missing required argument") end
                    local pool = to_array(args[1])
                    local n = #pool
                    local raw_r = (args[2] ~= nil) and args[2] or (kwargs and kwargs.r)
                    if raw_r == nil then Runtime.raise("TypeError", "combinations_with_replacement() missing required argument 'r'") end
                    local r = to_int(raw_r)
                    if r < 0 then Runtime.raise("ValueError", "r must be non-negative") end

                    local done = (n == 0 and r > 0)
                    local first = true
                    local indices = {}
                    for i = 1, r do indices[i] = 1 end

                    return make_iter("combinations_with_replacement", function()
                        if done then Runtime.raise("StopIteration") end
                        if r == 0 then
                            done = true
                            return { __type = "tuple", elements = {} }
                        end
                        if first then
                            first = false
                        else
                            local piv = 0
                            for i = r, 1, -1 do
                                if indices[i] ~= n then
                                    piv = i
                                    break
                                end
                            end
                            if piv == 0 then
                                done = true
                                Runtime.raise("StopIteration")
                            end
                            local next_v = indices[piv] + 1
                            for j = piv, r do
                                indices[j] = next_v
                            end
                        end
                        local res = {}
                        for i = 1, r do res[i] = pool[indices[i]] end
                        return { __type = "tuple", elements = res }
                    end)
                end
            }
            return cache.combinations_with_replacement
        end

        return nil
    end

    local mod_dict = {
        __getattr__ = {
            __type = "builtin_method",
            __call = function(args)
                local self, a = args[1], args[2]
                local val = get_attr(a)
                if val ~= nil then
                    self.__dict[a] = val
                    return val
                end
                Runtime.raise("AttributeError", "module 'itertools' has no attribute '" .. tostring(a) .. "'")
            end
        }
    }
    local mod = PyClass.make_instance(PyClass.make_class("module", {}, mod_dict))
    mod.__dict["__name__"] = "itertools"
    return mod
end

-- ==========================================
-- 2. functools
-- ==========================================
local function init_functools_module()
    local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
    local cache = {}

    local WRAPPER_ASSIGNMENTS = {
        __type = "tuple",
        elements = { "__module__", "__name__", "__qualname__", "__doc__", "__annotations__" }
    }
    local WRAPPER_UPDATES = {
        __type = "tuple",
        elements = { "__dict__" }
    }

    -- 輔助:將任意可雜湊 Python 參數序列化為唯一快取鍵值 (同時檢查不可雜湊型別)
    local function serialize_hashable(val, typed)
        local t = type(val)
        if t == "number" then
            if val ~= val then return "NaN" end
            if typed then
                local is_int = (math.floor(val) == val and math.abs(val) <= 9007199254740990)
                return (is_int and "I:" or "F:") .. tostring(val)
            end
            return "N:" .. tostring(val)
        elseif t == "boolean" then
            if typed then return val and "B:1" or "B:0" end
            return val and "N:1" or "N:0"
        elseif t == "string" then
            return "S#" .. #val .. ":" .. val
        elseif val == nil or (t == "table" and val.__class and val.__class.__name == "NoneType") then
            return "None"
        elseif t == "table" then
            local pt = val.__type
            if pt == "list" or pt == "dict" or pt == "set" or pt == "bytearray" then
                Runtime.raise("TypeError", "unhashable type: '" .. pt .. "'")
            elseif pt == "int" and val.is_big then
                local MathCore = require("Module:沙盒/a2569875/PythonScript/Runtime/Cmath")
                return (typed and "I:" or "N:") .. MathCore.BigInt.to_string(val.sign, val.limbs)
            elseif pt == "tuple" then
                local parts = {}
                for i = 1, #val.elements do
                    parts[i] = serialize_hashable(val.elements[i], typed)
                end
                return "T#" .. #val.elements .. "(" .. table.concat(parts, ",") .. ")"
            elseif pt == "frozenset" then
                local parts = {}
                for i = 1, #val.elements do
                    parts[i] = serialize_hashable(val.elements[i], typed)
                end
                table.sort(parts)
                return "FS#" .. #val.elements .. "{" .. table.concat(parts, ",") .. "}"
            elseif pt == "bytes" or pt == "memoryview" then
                return "BY#" .. #val._data .. ":" .. val._data
            elseif pt == "complex" then
                if val.imag == 0 and not typed then return "N:" .. tostring(val.real) end
                return "C:" .. tostring(val.real) .. ":" .. tostring(val.imag)
            else
                Runtime.builtins.hash({val})
                return "O:" .. tostring(val)
            end
        end
        return "U:" .. tostring(val)
    end

    local function make_cache_key(args, kwargs, typed)
        local n_args = args and #args or 0
        local has_kw = kwargs and next(kwargs) ~= nil
        -- 極速通道:單一數值參數且無 kwargs 及 typed=False 時,直接使用 Lua number 作為索引
        if not typed and not has_kw and n_args == 1 then
            local a1 = args[1]
            if type(a1) == "number" and a1 == a1 then
                return a1
            end
        end
        local parts = {}
        for i = 1, n_args do
            parts[i] = serialize_hashable(args[i], typed)
        end
        if has_kw then
            local kw_keys = {}
            for k, _ in pairs(kwargs) do table.insert(kw_keys, k) end
            table.sort(kw_keys)
            table.insert(parts, "|KW|")
            for _, k in ipairs(kw_keys) do
                table.insert(parts, "K#" .. #k .. ":" .. k .. "=" .. serialize_hashable(kwargs[k], typed))
            end
        end
        return "K:" .. table.concat(parts, ";")
    end

    local CacheInfoClass = nil
    local function get_cache_info_class()
        if not CacheInfoClass then
            local collections_mod = Librarys.get_module("collections")
            local nt_fn = PyClass.get_attr(collections_mod, "namedtuple")
            CacheInfoClass = nt_fn.__call({
                "CacheInfo",
                { __type = "list", elements = { "hits", "misses", "maxsize", "currsize" } }
            })
        end
        return CacheInfoClass
    end

    local function copy_wrapper_meta(wrapper, wrapped)
        wrapper.__dict = wrapper.__dict or {}
        for _, attr in ipairs({ "__module__", "__name__", "__qualname__", "__doc__" }) do
            local ok, v = pcall(PyClass.get_attr, wrapped, attr)
            if ok and v ~= nil then
                PyClass.raw_set_attr(wrapper, attr, v)
            end
        end
        PyClass.raw_set_attr(wrapper, "__wrapped__", wrapped)
        return wrapper
    end

    local function create_lru_wrapper(user_func, maxsize, typed)
        if not Runtime.builtins.callable({user_func}) then
            Runtime.raise("TypeError", "the first argument must be callable")
        end

        local hits, misses, currsize = 0, 0, 0
        local cache_map = {}
        -- 雙向環狀鏈結串列 (僅在有限 maxsize > 0 時啟用)
        local root = {}
        root.prev = root
        root.next = root

        local wrapper = {
            __type = "builtin_method",
            __name = (type(user_func) == "table" and (user_func.__name or user_func.__qualname__)) or "<cached_func>",
            __dict = {}
        }

        if maxsize == nil then
            -- 無上限快取 (@cache 或 maxsize=None):免維護鏈結串列,極致高速
            wrapper.__call = function(args, kwargs)
                local key = make_cache_key(args, kwargs, typed)
                local cached = cache_map[key]
                if cached ~= nil then
                    hits = hits + 1
                    return cached
                end
                misses = misses + 1
                local res = Evaluator.invoke(user_func, args, kwargs)
                if res == nil then res = Runtime.builtins.None end
                if cache_map[key] == nil then currsize = currsize + 1 end
                cache_map[key] = res
                return res
            end
        elseif maxsize == 0 then
            -- maxsize == 0:停用快取,僅統計 misses
            wrapper.__call = function(args, kwargs)
                make_cache_key(args, kwargs, typed)
                misses = misses + 1
                return Evaluator.invoke(user_func, args, kwargs)
            end
        else
            -- 具容量上限的 LRU 快取 (O(1) 雙向鏈結串列更新與淘汰)
            wrapper.__call = function(args, kwargs)
                local key = make_cache_key(args, kwargs, typed)
                local node = cache_map[key]
                if node ~= nil then
                    hits = hits + 1
                    node.prev.next = node.next
                    node.next.prev = node.prev
                    local last = root.prev
                    last.next = node
                    root.prev = node
                    node.prev = last
                    node.next = root
                    return node.val
                end
                misses = misses + 1
                local res = Evaluator.invoke(user_func, args, kwargs)
                if res == nil then res = Runtime.builtins.None end

                node = cache_map[key]
                if node ~= nil then
                    node.prev.next = node.next
                    node.next.prev = node.prev
                    local last = root.prev
                    last.next = node
                    root.prev = node
                    node.prev = last
                    node.next = root
                    node.val = res
                elseif currsize >= maxsize then
                    local oldest = root.next
                    cache_map[oldest.key] = nil
                    oldest.key = key
                    oldest.val = res
                    root = oldest
                    cache_map[key] = oldest
                else
                    local last = root.prev
                    local new_node = { prev = last, next = root, key = key, val = res }
                    last.next = new_node
                    root.prev = new_node
                    cache_map[key] = new_node
                    currsize = currsize + 1
                end
                return res
            end
        end

        copy_wrapper_meta(wrapper, user_func)

        local py_maxsize = (maxsize == nil) and Runtime.builtins.None or maxsize
        wrapper.__dict.cache_info = {
            __type = "builtin_method",
            __name = "cache_info",
            __call = function()
                local cls = get_cache_info_class()
                return Evaluator.invoke(cls, { hits, misses, py_maxsize, currsize }, {})
            end
        }
        wrapper.__dict.cache_clear = {
            __type = "builtin_method",
            __name = "cache_clear",
            __call = function()
                cache_map = {}
                hits, misses, currsize = 0, 0, 0
                root.prev = root
                root.next = root
                return Runtime.builtins.None
            end
        }
        wrapper.__dict.cache_parameters = {
            __type = "builtin_method",
            __name = "cache_parameters",
            __call = function()
                return {
                    __type = "dict",
                    pairs = {
                        { key = "maxsize", value = py_maxsize },
                        { key = "typed", value = typed }
                    }
                }
            end
        }
        return wrapper
    end

    local function get_attr(attr)
        if cache[attr] ~= nil then return cache[attr] end

        if attr == "WRAPPER_ASSIGNMENTS" then
            cache[attr] = WRAPPER_ASSIGNMENTS
            return WRAPPER_ASSIGNMENTS
        elseif attr == "WRAPPER_UPDATES" then
            cache[attr] = WRAPPER_UPDATES
            return WRAPPER_UPDATES

        elseif attr == "reduce" then
            cache.reduce = {
                __type = "builtin_method",
                __name = "reduce",
                __call = function(args, kwargs)
                    if kwargs and next(kwargs) then
                        Runtime.raise("TypeError", "reduce() takes no keyword arguments")
                    end
                    if not args or #args < 2 or #args > 3 then
                        Runtime.raise("TypeError", "reduce expected 2 or 3 arguments, got " .. (args and #args or 0))
                    end
                    local func, iterable = args[1], args[2]
                    local iter_obj = Runtime.builtins.iter({iterable})
                    local sentinel = {}
                    local acc
                    if #args == 3 then
                        acc = args[3]
                    else
                        acc = Runtime.builtins.next({iter_obj, sentinel})
                        if acc == sentinel then
                            Runtime.raise("TypeError", "reduce() of empty iterable with no initial value")
                        end
                    end
                    while true do
                        local v = Runtime.builtins.next({iter_obj, sentinel})
                        if v == sentinel then break end
                        acc = Evaluator.invoke(func, {acc, v}, {})
                    end
                    return acc
                end
            }
            return cache.reduce

        elseif attr == "lru_cache" then
            cache.lru_cache = {
                __type = "builtin_method",
                __name = "lru_cache",
                __call = function(args, kwargs)
                    -- 支援直接裝飾 @lru_cache (不帶括號)
                    if args and #args == 1 and (not kwargs or next(kwargs) == nil) and Runtime.builtins.callable({args[1]}) then
                        return create_lru_wrapper(args[1], 128, false)
                    end

                    local raw_max = 128
                    local has_max_arg = false
                    if args and #args >= 1 then
                        raw_max = args[1]; has_max_arg = true
                    elseif kwargs and kwargs.maxsize ~= nil then
                        raw_max = kwargs.maxsize; has_max_arg = true
                    end

                    local maxsize = 128
                    if has_max_arg then
                        if raw_max == nil or (type(raw_max) == "table" and raw_max.__class and raw_max.__class.__name == "NoneType") then
                            maxsize = nil
                        elseif type(raw_max) == "number" and math.floor(raw_max) == raw_max and type(raw_max) ~= "boolean" then
                            maxsize = math.max(0, raw_max)
                        elseif type(raw_max) == "table" and raw_max.__type == "int" then
                            maxsize = math.max(0, raw_max.value or 0)
                        else
                            Runtime.raise("TypeError", "Expected first argument to be an integer, a callable, or None")
                        end
                    end

                    local raw_typed = (args and args[2] ~= nil and args[2]) or (kwargs and kwargs.typed) or false
                    local typed = Runtime.builtins.bool.__call({raw_typed})

                    return {
                        __type = "builtin_method",
                        __name = "lru_cache_decorator",
                        __call = function(d_args)
                            if not d_args or #d_args < 1 then
                                Runtime.raise("TypeError", "lru_cache decorator requires a function argument")
                            end
                            return create_lru_wrapper(d_args[1], maxsize, typed)
                        end
                    }
                end
            }
            return cache.lru_cache

        elseif attr == "cache" then
            cache.cache = {
                __type = "builtin_method",
                __name = "cache",
                __call = function(args)
                    if not args or #args < 1 then
                        Runtime.raise("TypeError", "cache() missing required argument 'user_function'")
                    end
                    return create_lru_wrapper(args[1], nil, false)
                end
            }
            return cache.cache

        elseif attr == "update_wrapper" then
            cache.update_wrapper = {
                __type = "builtin_method",
                __name = "update_wrapper",
                __call = function(args, kwargs)
                    local wrapper, wrapped = args and args[1], args and args[2]
                    if not wrapper or not wrapped then
                        Runtime.raise("TypeError", "update_wrapper() missing required arguments")
                    end
                    local assigned = (args and args[3]) or (kwargs and kwargs.assigned) or WRAPPER_ASSIGNMENTS
                    for _, field in ipairs(assigned.elements or {}) do
                        local ok, val = pcall(PyClass.get_attr, wrapped, field)
                        if ok and val ~= nil then
                            pcall(PyClass.set_attr, wrapper, field, val)
                        end
                    end
                    if type(wrapped) == "table" and wrapped.__dict then
                        wrapper.__dict = wrapper.__dict or {}
                        for k, v in pairs(wrapped.__dict) do
                            wrapper.__dict[k] = v
                        end
                    end
                    pcall(PyClass.set_attr, wrapper, "__wrapped__", wrapped)
                    return wrapper
                end
            }
            return cache.update_wrapper

        elseif attr == "wraps" then
            local uw = get_attr("update_wrapper")
            cache.wraps = {
                __type = "builtin_method",
                __name = "wraps",
                __call = function(args, kwargs)
                    local wrapped = args and args[1]
                    if not wrapped then
                        Runtime.raise("TypeError", "wraps() missing required argument 'wrapped'")
                    end
                    local assigned = (args and args[2]) or (kwargs and kwargs.assigned) or WRAPPER_ASSIGNMENTS
                    local updated = (args and args[3]) or (kwargs and kwargs.updated) or WRAPPER_UPDATES
                    return {
                        __type = "builtin_method",
                        __name = "wraps_decorator",
                        __call = function(d_args)
                            return uw.__call({ d_args[1], wrapped, assigned, updated }, {})
                        end
                    }
                end
            }
            return cache.wraps

        elseif attr == "partial" then
            local partial_dict = {
                __init__ = {
                    __type = "builtin_method",
                    __call = function(args, kwargs)
                        if not args or #args < 2 then
                            Runtime.raise("TypeError", "type 'partial' takes at least one argument")
                        end
                        local self, func = args[1], args[2]
                        if not Runtime.builtins.callable({func}) then
                            Runtime.raise("TypeError", "the first argument must be callable")
                        end
                        local p_args = {}
                        local p_kwargs = {}

                        -- 若傳入的 func 本身也是 partial 實例,自動展平合併
                        if type(func) == "table" and func.__type == "instance" and func.__class == self.__class then
                            for _, v in ipairs(func.__dict.args.elements) do table.insert(p_args, v) end
                            for _, p in ipairs(func.__dict.keywords.pairs) do p_kwargs[p.key] = p.value end
                            func = func.__dict.func
                        end

                        for i = 3, #args do table.insert(p_args, args[i]) end
                        if kwargs then
                            for k, v in pairs(kwargs) do p_kwargs[k] = v end
                        end

                        local kw_pairs = {}
                        for k, v in pairs(p_kwargs) do table.insert(kw_pairs, { key = k, value = v }) end

                        self.__dict.func = func
                        self.__dict.args = { __type = "tuple", elements = p_args }
                        self.__dict.keywords = { __type = "dict", pairs = kw_pairs }
                        return nil
                    end
                },
                __call__ = {
                    __type = "builtin_method",
                    __call = function(args, kwargs)
                        local self = args[1]
                        local call_args = {}
                        for _, v in ipairs(self.__dict.args.elements) do table.insert(call_args, v) end
                        for i = 2, #args do table.insert(call_args, args[i]) end

                        local call_kwargs = {}
                        for _, p in ipairs(self.__dict.keywords.pairs) do call_kwargs[p.key] = p.value end
                        if kwargs then
                            for k, v in pairs(kwargs) do call_kwargs[k] = v end
                        end
                        return Evaluator.invoke(self.__dict.func, call_args, call_kwargs)
                    end
                },
                __repr__ = {
                    __type = "builtin_method",
                    __call = function(args)
                        local self = args[1]
                        local parts = { Runtime.builtins.repr({self.__dict.func}) }
                        for _, v in ipairs(self.__dict.args.elements) do
                            table.insert(parts, Runtime.builtins.repr({v}))
                        end
                        for _, p in ipairs(self.__dict.keywords.pairs) do
                            table.insert(parts, tostring(p.key) .. "=" .. Runtime.builtins.repr({p.value}))
                        end
                        return "functools.partial(" .. table.concat(parts, ", ") .. ")"
                    end
                }
            }
            partial_dict.__str__ = partial_dict.__repr__
            cache.partial = PyClass.make_class("partial", {}, partial_dict)
            return cache.partial

        elseif attr == "cmp_to_key" then
            cache.cmp_to_key = {
                __type = "builtin_method",
                __name = "cmp_to_key",
                __call = function(args, kwargs)
                    local mycmp = (args and args[1]) or (kwargs and kwargs.mycmp)
                    if not mycmp then
                        Runtime.raise("TypeError", "cmp_to_key() missing required argument 'mycmp'")
                    end
                    local K_dict = {
                        __init__ = { __type = "builtin_method", __call = function(a) a[1].__dict.obj = a[2]; return nil end },
                        __lt__ = { __type = "builtin_method", __call = function(a) return Evaluator.invoke(mycmp, {a[1].__dict.obj, a[2].__dict.obj}, {}) < 0 end },
                        __gt__ = { __type = "builtin_method", __call = function(a) return Evaluator.invoke(mycmp, {a[1].__dict.obj, a[2].__dict.obj}, {}) > 0 end },
                        __eq__ = { __type = "builtin_method", __call = function(a) return Evaluator.invoke(mycmp, {a[1].__dict.obj, a[2].__dict.obj}, {}) == 0 end },
                        __le__ = { __type = "builtin_method", __call = function(a) return Evaluator.invoke(mycmp, {a[1].__dict.obj, a[2].__dict.obj}, {}) <= 0 end },
                        __ge__ = { __type = "builtin_method", __call = function(a) return Evaluator.invoke(mycmp, {a[1].__dict.obj, a[2].__dict.obj}, {}) >= 0 end },
                        __ne__ = { __type = "builtin_method", __call = function(a) return Evaluator.invoke(mycmp, {a[1].__dict.obj, a[2].__dict.obj}, {}) ~= 0 end }
                    }
                    return PyClass.make_class("KeyWrapper", {}, K_dict)
                end
            }
            return cache.cmp_to_key
        end

        return nil
    end

    local mod_dict = {
        __getattr__ = {
            __type = "builtin_method",
            __call = function(args)
                local self, a = args[1], args[2]
                local val = get_attr(a)
                if val ~= nil then
                    self.__dict[a] = val
                    return val
                end
                Runtime.raise("AttributeError", "module 'functools' has no attribute '" .. tostring(a) .. "'")
            end
        }
    }
    local mod = PyClass.make_instance(PyClass.make_class("module", {}, mod_dict))
    mod.__dict["__name__"] = "functools"
    return mod
end

-- ==========================================
-- 3. collections
-- ==========================================
function Librarys.init_collections_module(module_name,env_vars)
    local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
    local B = Runtime.builtins

	-- ==========================================
    -- 1. collections.abc 模組與結構化型別檢查補丁
    -- ==========================================
    local abc_dict = {}

    local function make_abc(name, required_methods)
        local cls = PyClass.make_class(name, { B.object }, {})
        cls.__dict["__module__"] = "collections.abc"
        cls._abc_name = name
        cls._required_methods = required_methods
        abc_dict[name] = cls
        return cls
    end

    -- 註冊核心 ABC 介面與其要求的魔術方法
    local Iterable = make_abc("Iterable", { "__iter__" })
    local Iterator = make_abc("Iterator", { "__iter__", "__next__" })
    local Generator = make_abc("Generator", { "send", "throw" })
    local Sized = make_abc("Sized", { "__len__" })
    local Container = make_abc("Container", { "__contains__" })
    local Callable = make_abc("Callable", { "__call__" })
    local Collection = make_abc("Collection", { "__iter__", "__len__", "__contains__" })
    local Sequence = make_abc("Sequence", { "__getitem__", "__len__" })
    local MutableSequence = make_abc("MutableSequence", { "__getitem__", "__setitem__", "__delitem__", "__len__", "insert" })
    local Mapping = make_abc("Mapping", { "__getitem__", "keys", "items", "values", "__len__" })
    local MutableMapping = make_abc("MutableMapping", { "__getitem__", "__setitem__", "__delitem__", "keys" })
    local Set = make_abc("Set", { "__and__", "__or__", "__sub__", "__xor__" })
    local MutableSet = make_abc("MutableSet", { "add", "discard" })

    -- 【黑魔法補丁】:攔截 isinstance 與 issubclass,支援鴨子型別與原生型別速查
    if not B._abc_patched then
        B._abc_patched = true
        local orig_isinstance = B.isinstance
        local orig_issubclass = B.issubclass

        local function check_structural(obj, cls, is_sub)
            if type(cls) == "table" and cls._abc_name then
                local t_name
                if is_sub then
                    t_name = (type(obj) == "table" and obj.__type == "class" and obj.__name) or ""
                else
                    if type(obj) == "table" then
                        t_name = obj.__type
                        if t_name == "instance" and obj.__class then t_name = obj.__class.__name end
                    elseif type(obj) == "string" then t_name = "str"
                    elseif type(obj) == "number" then t_name = (math.floor(obj) == obj) and "int" or "float"
                    elseif type(obj) == "boolean" then t_name = "bool"
                    end
                end

                -- 1. 原生高速比對路由 (完全吻合 CPython 對 builtins 的分類)
                local n = cls._abc_name
                if n == "Iterable" or n == "Container" or n == "Collection" then
                    if t_name == "list" or t_name == "tuple" or t_name == "dict" or t_name == "set" or t_name == "frozenset" or t_name == "str" or t_name == "bytes" or t_name == "bytearray" or t_name == "memoryview" or t_name == "range" then return true end
                elseif n == "Sequence" then
                    if t_name == "list" or t_name == "tuple" or t_name == "str" or t_name == "bytes" or t_name == "bytearray" or t_name == "memoryview" or t_name == "range" then return true end
                elseif n == "MutableSequence" then
                    if t_name == "list" or t_name == "bytearray" then return true end
                elseif n == "Mapping" or n == "MutableMapping" then
                    if t_name == "dict" then return true end
                elseif n == "Set" then
                    if t_name == "set" or t_name == "frozenset" then return true end
                elseif n == "MutableSet" then
                    if t_name == "set" then return true end
                elseif n == "Callable" then
                    if type(obj) == "function" then return true end
                    if type(obj) == "table" and (obj.__type == "function" or obj.__type == "bound_method" or obj.__type == "builtin_method" or obj.__type == "class") then return true end
                    if t_name == "instance" then
                        local ok, attr = pcall(PyClass.get_attr, obj, "__call__")
                        if ok and attr then return true end
                    end
                end

                -- 2. 使用者自定義類別的鴨子型別 (Duck Typing) 檢查
                if cls._required_methods then
                    for _, method in ipairs(cls._required_methods) do
                        local ok, attr
                        if is_sub then
                            ok, attr = pcall(PyClass.lookup_class_attr, obj, method)
                        else
                            ok, attr = pcall(PyClass.get_attr, obj, method)
                        end
                        if not ok or not attr then return false end
                    end
                    return true
                end
            end
            return nil
        end

        local function custom_isinstance(obj, cls)
            if cls == nil then Runtime.raise("TypeError", "isinstance() arg 2 must be a type or tuple of types") end
            if type(cls) == "table" and cls.__type == "tuple" then
                for _, c in ipairs(cls.elements) do
                    if custom_isinstance(obj, c) then return true end
                end
                return false
            end
            local res = check_structural(obj, cls, false)
            if res ~= nil then return res end
            return orig_isinstance({ obj, cls })
        end

        local function custom_issubclass(child, parent)
            if type(parent) == "table" and parent.__type == "tuple" then
                for _, p in ipairs(parent.elements) do
                    if custom_issubclass(child, p) then return true end
                end
                return false
            end
            local res = check_structural(child, parent, true)
            if res ~= nil then return res end
            return orig_issubclass({ child, parent })
        end

        B.isinstance = function(args) return custom_isinstance(args[1], args[2]) end
        B.issubclass = function(args) return custom_issubclass(args[1], args[2]) end
    end

    local abc_mod = PyClass.make_instance(PyClass.make_class("module", {}, {}))
    abc_mod.__dict["__name__"] = "collections.abc"
    for k, v in pairs(abc_dict) do abc_mod.__dict[k] = v end

    local Sys = require("Module:沙盒/a2569875/PythonScript/Librarys/Argparse").Sys
    Sys.modules_registry["collections.abc"] = abc_mod
	
    local function _collections_eq(a, b)
        if type(a) == "table" and type(b) == "table" then
            if a.__type == "list" and b.__type == "list" then
                if #a.elements ~= #b.elements then return false end
                for i = 1, #a.elements do if not _collections_eq(a.elements[i], b.elements[i]) then return false end end
                return true
            end
        end; return a == b
    end

    local cache = {}
    
    -- 核心建構器:被存取時才動態消耗記憶體建立物件
    local function get_attr(attr)
        if cache[attr] then return cache[attr] end
        
        if attr == "UserDict" then
            local UserDict_dict = {
                __init__ = { __type = "builtin_method", __call = function(args, kwargs)
                    local self = args[1]
                    self.__dict.data = Runtime.builtins.dict.__call({args[2]}, kwargs)
                    return nil
                end},
                __getitem__ = { __type = "builtin_method", __call = function(args)
                    local self, key = args[1], args[2]
                    for _, p in ipairs(self.__dict.data.pairs) do if _collections_eq(p.key, key) then return p.value end end
                    local success, missing = pcall(PyClass.get_attr, self, "__missing__")
                    if success and missing and missing.__type == "bound_method" then
                        return Evaluator.invoke(missing.__func, {key}, {}, missing.__self)
                    end
                    Runtime.raise("KeyError", tostring(key))
                end},
                __setitem__ = { __type = "builtin_method", __call = function(args)
                    local self, key, val = args[1], args[2], args[3]; local found = false
                    for _, p in ipairs(self.__dict.data.pairs) do if _collections_eq(p.key, key) then p.value = val; found = true; break end end
                    if not found then table.insert(self.__dict.data.pairs, {key=key, value=val}) end; return nil
                end},
                __delitem__ = { __type = "builtin_method", __call = function(args)
                    local self, key = args[1], args[2]
                    for i, p in ipairs(self.__dict.data.pairs) do if _collections_eq(p.key, key) then table.remove(self.__dict.data.pairs, i); return nil end end
                    Runtime.raise("KeyError", tostring(key))
                end},
                __iter__ = { __type = "builtin_method", __call = function(args) return Runtime.builtins.iter({args[1].__dict.data}) end},
                __len__ = { __type = "builtin_method", __call = function(args) return #args[1].__dict.data.pairs end},
                __contains__ = { __type = "builtin_method", __call = function(args)
                    for _, p in ipairs(args[1].__dict.data.pairs) do if _collections_eq(p.key, args[2]) then return true end end; return false
                end},
                __repr__ = { __type = "builtin_method", __call = function(args) return args[1].__class.__name .. "(" .. Runtime.builtins.repr({args[1].__dict.data}) .. ")" end},
                keys = { __type = "builtin_method", __call = function(args) return PyClass.get_attr(args[1].__dict.data, "keys").__call({}) end},
                values = { __type = "builtin_method", __call = function(args) return PyClass.get_attr(args[1].__dict.data, "values").__call({}) end},
                items = { __type = "builtin_method", __call = function(args) return PyClass.get_attr(args[1].__dict.data, "items").__call({}) end},
                get = { __type = "builtin_method", __call = function(args) return PyClass.get_attr(args[1].__dict.data, "get").__call({args[2], args[3]}) end},
                clear = { __type = "builtin_method", __call = function(args) return PyClass.get_attr(args[1].__dict.data, "clear").__call({}) end},
                update = { __type = "builtin_method", __call = function(args, kwargs) return PyClass.get_attr(args[1].__dict.data, "update").__call({args[2]}, kwargs) end},
                pop = { __type = "builtin_method", __call = function(args) return PyClass.get_attr(args[1].__dict.data, "pop").__call({args[2], args[3]}) end},
                copy = { __type = "builtin_method", __call = function(args)
                    local inst = PyClass.make_instance(args[1].__class)
                    inst.__dict.data = PyClass.get_attr(args[1].__dict.data, "copy").__call({}); return inst
                end}
            }
            UserDict_dict.__str__ = UserDict_dict.__repr__
            cache.UserDict = PyClass.make_class("UserDict", {}, UserDict_dict)
            return cache.UserDict
        end
        
		if attr == "Counter" then
            local UserDict = get_attr("UserDict")
            local Counter_dict = {
                __init__ = { __type = "builtin_method", __call = function(args, kwargs)
                    local self = args[1]
                    self.__dict.data = {__type="dict", pairs={}}
                    -- 使用 Evaluator.invoke 呼叫 update
                    if args[2] then Evaluator.invoke(PyClass.get_attr(self, "update"), {args[2]}, kwargs)
                    elseif kwargs and next(kwargs) then Evaluator.invoke(PyClass.get_attr(self, "update"), {}, kwargs) end
                    return nil
                end},
                __missing__ = { __type = "builtin_method", __call = function(args) return 0 end},
                elements = { __type = "builtin_method", __call = function(args)
                    local elts = {}
                    for _, p in ipairs(args[1].__dict.data.pairs) do
                        if type(p.value) == "number" and p.value > 0 then
                            for i = 1, math.floor(p.value) do table.insert(elts, p.key) end
                        end
                    end; return Runtime.builtins.iter({{__type="list", elements=elts}})
                end},
                most_common = { __type = "builtin_method", __call = function(args)
                    local n = args[2]; local items = {}
                    for _, p in ipairs(args[1].__dict.data.pairs) do table.insert(items, {p.key, p.value}) end
                    table.sort(items, function(a, b) return a[2] > b[2] end)
                    local res = {}; local limit = n or #items
                    for i = 1, limit do if i > #items then break end table.insert(res, {__type="tuple", elements={items[i][1], items[i][2]}}) end
                    return {__type="list", elements=res}
                end},
                total = { __type = "builtin_method", __call = function(args)
                    local sum = 0; for _, p in ipairs(args[1].__dict.data.pairs) do sum = sum + p.value end return sum
                end},
                update = { __type = "builtin_method", __call = function(args, kwargs)
                    local self, iterable = args[1], args[2]
                    local function add_count(k, v)
                        local cur = 0
                        for _, p in ipairs(self.__dict.data.pairs) do if _collections_eq(p.key, k) then cur = p.value; break end end
                        -- 使用 Evaluator.invoke 呼叫 __setitem__
                        Evaluator.invoke(PyClass.get_attr(self, "__setitem__"), {k, cur + v}, {})
                    end
                    if iterable then
                        if type(iterable) == "table" and iterable.__type == "dict" then
                            for _, p in ipairs(iterable.pairs) do add_count(p.key, p.value) end
                        elseif type(iterable) == "table" and iterable.__type == "instance" and PyClass.is_instance(iterable, UserDict) then
                            for _, p in ipairs(iterable.__dict.data.pairs) do add_count(p.key, p.value) end
                        else
                            local iter_obj = Runtime.builtins.iter({iterable}); local next_fn = PyClass.get_attr(iter_obj, "__next__")
                            while true do
                                local success, v = pcall(Evaluator.invoke, next_fn.__func, {}, {}, next_fn.__self)
                                if not success then break end; add_count(v, 1)
                            end
                        end
                    end
                    if kwargs then for k, v in pairs(kwargs) do add_count(k, v) end end; return nil
                end},
                subtract = { __type = "builtin_method", __call = function(args, kwargs)
                    local self, iterable = args[1], args[2]
                    local function sub_count(k, v)
                        local cur = 0
                        for _, p in ipairs(self.__dict.data.pairs) do if _collections_eq(p.key, k) then cur = p.value; break end end
                        -- 使用 Evaluator.invoke 呼叫 __setitem__
                        Evaluator.invoke(PyClass.get_attr(self, "__setitem__"), {k, cur - v}, {})
                    end
                    if iterable then
                        if type(iterable) == "table" and iterable.__type == "dict" then
                            for _, p in ipairs(iterable.pairs) do sub_count(p.key, p.value) end
                        else
                            local iter_obj = Runtime.builtins.iter({iterable}); local next_fn = PyClass.get_attr(iter_obj, "__next__")
                            while true do
                                local success, v = pcall(Evaluator.invoke, next_fn.__func, {}, {}, next_fn.__self)
                                if not success then break end; sub_count(v, 1)
                            end
                        end
                    end
                    if kwargs then for k, v in pairs(kwargs) do sub_count(k, v) end end; return nil
                end}
            }
            cache.Counter = PyClass.make_class("Counter", {UserDict}, Counter_dict)
            return cache.Counter
        end
        
		if attr == "defaultdict" then
            local UserDict = get_attr("UserDict")
            local defaultdict_dict = {
                __init__ = { __type = "builtin_method", __call = function(args, kwargs)
                    local self = args[1]; self.__dict.default_factory = args[2]
                    self.__dict.data = {__type="dict", pairs={}}
                    if args[3] then PyClass.get_attr(self.__dict.data, "update").__call({args[3]}, kwargs) end
                    return nil
                end},
                __missing__ = { __type = "builtin_method", __call = function(args)
                    local self, key = args[1], args[2]
                    if not self.__dict.default_factory or self.__dict.default_factory == Runtime.builtins.None then Runtime.raise("KeyError", tostring(key)) end
                    local val = Evaluator.invoke(self.__dict.default_factory, {}, {})
                    -- 使用 Evaluator.invoke 呼叫 __setitem__
                    Evaluator.invoke(PyClass.get_attr(self, "__setitem__"), {key, val}, {})
                    return val
                end}
            }
            cache.defaultdict = PyClass.make_class("defaultdict", {UserDict}, defaultdict_dict)
            return cache.defaultdict
        end
        
        if attr == "OrderedDict" then
            local UserDict = get_attr("UserDict")
            local OrderedDict_dict = {
                move_to_end = { __type = "builtin_method", __call = function(args)
                    local self, key, last = args[1], args[2], args[3]
                    if last == nil then last = true else last = Runtime.builtins.bool.__call({last}) end
                    local pairs_list = self.__dict.data.pairs
                    for i, p in ipairs(pairs_list) do
                        if _collections_eq(p.key, key) then
                            local item = table.remove(pairs_list, i)
                            if last then table.insert(pairs_list, item) else table.insert(pairs_list, 1, item) end
                            return nil
                        end
                    end
                    Runtime.raise("KeyError", tostring(key))
                end},
                popitem = { __type = "builtin_method", __call = function(args)
                    local self, last = args[1], args[2]
                    if last == nil then last = true else last = Runtime.builtins.bool.__call({last}) end
                    local pairs_list = self.__dict.data.pairs
                    if #pairs_list == 0 then Runtime.raise("KeyError", "dictionary is empty") end
                    local idx = last and #pairs_list or 1
                    local item = table.remove(pairs_list, idx)
                    return {__type="tuple", elements={item.key, item.value}}
                end}
            }
            cache.OrderedDict = PyClass.make_class("OrderedDict", {UserDict}, OrderedDict_dict)
            return cache.OrderedDict
        end
        
		if attr == "deque" then
            local deque_dict = {
                __init__ = { __type = "builtin_method", __call = function(args, kwargs)
                    local self, iterable = args[1], args[2]
                    self.__dict.maxlen = (kwargs and kwargs.maxlen) or Runtime.builtins.None
                    self.__dict.data = {__type="list", elements={}}
                    -- 使用 Evaluator.invoke 呼叫 extend
                    if iterable then Evaluator.invoke(PyClass.get_attr(self, "extend"), {iterable}, {}) end
                    return nil
                end},
                append = { __type = "builtin_method", __call = function(args)
                    local self, x = args[1], args[2]; table.insert(self.__dict.data.elements, x)
                    if self.__dict.maxlen ~= Runtime.builtins.None and #self.__dict.data.elements > self.__dict.maxlen then table.remove(self.__dict.data.elements, 1) end
                    return nil
                end},
                appendleft = { __type = "builtin_method", __call = function(args)
                    local self, x = args[1], args[2]; table.insert(self.__dict.data.elements, 1, x)
                    if self.__dict.maxlen ~= Runtime.builtins.None and #self.__dict.data.elements > self.__dict.maxlen then table.remove(self.__dict.data.elements) end
                    return nil
                end},
                pop = { __type = "builtin_method", __call = function(args)
                    if #args[1].__dict.data.elements == 0 then Runtime.raise("IndexError", "pop from an empty deque") end
                    return table.remove(args[1].__dict.data.elements)
                end},
                popleft = { __type = "builtin_method", __call = function(args)
                    if #args[1].__dict.data.elements == 0 then Runtime.raise("IndexError", "pop from an empty deque") end
                    return table.remove(args[1].__dict.data.elements, 1)
                end},
                clear = { __type = "builtin_method", __call = function(args) args[1].__dict.data.elements = {}; return nil end},
                extend = { __type = "builtin_method", __call = function(args)
                    local iter_obj = Runtime.builtins.iter({args[2]}); local next_fn = PyClass.get_attr(iter_obj, "__next__")
                    while true do
                        local success, v = pcall(Evaluator.invoke, next_fn.__func, {}, {}, next_fn.__self)
                        -- 使用 Evaluator.invoke 呼叫 append
                        if not success then break end; Evaluator.invoke(PyClass.get_attr(args[1], "append"), {v}, {})
                    end; return nil
                end},
                extendleft = { __type = "builtin_method", __call = function(args)
                    local iter_obj = Runtime.builtins.iter({args[2]}); local next_fn = PyClass.get_attr(iter_obj, "__next__")
                    while true do
                        local success, v = pcall(Evaluator.invoke, next_fn.__func, {}, {}, next_fn.__self)
                        -- 使用 Evaluator.invoke 呼叫 appendleft
                        if not success then break end; Evaluator.invoke(PyClass.get_attr(args[1], "appendleft"), {v}, {})
                    end; return nil
                end},
                __len__ = { __type = "builtin_method", __call = function(args) return #args[1].__dict.data.elements end},
                __iter__ = { __type = "builtin_method", __call = function(args) return Runtime.builtins.iter({args[1].__dict.data}) end},
                __repr__ = { __type = "builtin_method", __call = function(args)
                    local inner = Runtime.builtins.repr({args[1].__dict.data}); inner = string.sub(inner, 2, -2)
                    local ml = args[1].__dict.maxlen ~= Runtime.builtins.None and (", maxlen=" .. tostring(args[1].__dict.maxlen)) or ""
                    return "deque([" .. inner .. "]" .. ml .. ")"
                end}
            }
            cache.deque = PyClass.make_class("deque", {}, deque_dict)
            return cache.deque
        end
        
        if attr == "ChainMap" then
            local UserDict = get_attr("UserDict")
            local ChainMap_dict = {
                __init__ = { __type = "builtin_method", __call = function(args)
                    local self = args[1]; self.__dict.maps = {__type="list", elements={}}
                    if #args > 1 then for i=2, #args do table.insert(self.__dict.maps.elements, args[i]) end
                    else table.insert(self.__dict.maps.elements, {__type="dict", pairs={}}) end
                    return nil
                end},
                __getitem__ = { __type = "builtin_method", __call = function(args)
                    local key = args[2]
                    for _, m in ipairs(args[1].__dict.maps.elements) do
                        local success, res = pcall(function()
                            if m.__type == "dict" then
                                for _, p in ipairs(m.pairs) do if _collections_eq(p.key, key) then return p.value end end; error()
                            else
                                return Evaluator.invoke(PyClass.get_attr(m, "__getitem__").__func, {key}, {}, m)
                            end
                        end)
                        if success then return res end
                    end
                    Runtime.raise("KeyError", tostring(key))
                end},
                __setitem__ = { __type = "builtin_method", __call = function(args)
                    local self, key, val = args[1], args[2], args[3]
                    local first_map = self.__dict.maps.elements[1]
                    if first_map.__type == "dict" then
                        local found = false
                        for _, p in ipairs(first_map.pairs) do if _collections_eq(p.key, key) then p.value = val; found = true; break end end
                        if not found then table.insert(first_map.pairs, {key=key, value=val}) end
                    else
                        local setitem = PyClass.get_attr(first_map, "__setitem__")
                        Evaluator.invoke(setitem.__func, {key, val}, {}, setitem.__self)
                    end; return nil
                end}
            }
            cache.ChainMap = PyClass.make_class("ChainMap", {UserDict}, ChainMap_dict)
            return cache.ChainMap
        end
        
        if attr == "UserList" then
            local UserList_dict = {
                __init__ = { __type = "builtin_method", __call = function(args)
                    local self = args[1]; self.__dict.data = Runtime.builtins.list.__call({args[2]}); return nil
                end},
                __len__ = { __type = "builtin_method", __call = function(args) return #args[1].__dict.data.elements end},
                __iter__ = { __type = "builtin_method", __call = function(args) return Runtime.builtins.iter({args[1].__dict.data}) end},
                __repr__ = { __type = "builtin_method", __call = function(args) return args[1].__class.__name .. "(" .. Runtime.builtins.repr({args[1].__dict.data}) .. ")" end}
            }
            cache.UserList = PyClass.make_class("UserList", {}, UserList_dict)
            return cache.UserList
        end
        
        if attr == "UserString" then
            local UserString_dict = {
                __init__ = { __type = "builtin_method", __call = function(args)
                    local self = args[1]; self.__dict.data = Runtime.builtins.str.__call({args[2]}); return nil
                end},
                __len__ = { __type = "builtin_method", __call = function(args) return mw.ustring.len(args[1].__dict.data) end},
                __iter__ = { __type = "builtin_method", __call = function(args) return Runtime.builtins.iter({args[1].__dict.data}) end},
                __repr__ = { __type = "builtin_method", __call = function(args) return Runtime.builtins.repr({args[1].__dict.data}) end},
                __str__ = { __type = "builtin_method", __call = function(args) return args[1].__dict.data end},
                
                -- 【新增】__getattr__ 動態攔截並轉發字串原生方法
                __getattr__ = { __type = "builtin_method", __call = function(args)
                    local self, attr_name = args[1], args[2]
                    
                    -- 將屬性請求轉發給內部的真實字串物件 (self.data)
                    local success, method_or_val = pcall(PyClass.get_attr, self.__dict.data, attr_name)
                    
                    if not success then
                        -- 找不到屬性時,拋出符合當前代理類別名稱的 AttributeError
                        require("Module:沙盒/a2569875/PythonScript/Runtime").raise("AttributeError", "'" .. self.__class.__name .. "' object has no attribute '" .. attr_name .. "'")
                    end
                    
                    -- 若取得的是字串的原生內建方法 (如 upper, replace, split 等)
                    if type(method_or_val) == "table" and method_or_val.__type == "builtin_method" then
                        return {
                            __type = "builtin_method",
                            __call = function(m_args, m_kwargs)
                                -- 呼叫內建字串方法
                                local res = method_or_val.__call(m_args, m_kwargs)
                                
                                -- 依照 CPython 規範:若方法回傳的結果是字串,必須將其重新包裝回 UserString (或繼承的子類別) 實例
                                if type(res) == "string" then
                                    return Evaluator.invoke(self.__class, {res}, {})
                                end
                                
                                -- 若回傳非字串 (例如 split 回傳 list,或是 count 回傳 int),則直接返回原始結果
                                return res
                            end
                        }
                    end
                    
                    return method_or_val
                end}
            }
            cache.UserString = PyClass.make_class("UserString", {}, UserString_dict)
            return cache.UserString
        end
        
        if attr == "namedtuple" then
            cache.namedtuple = { __type = "builtin_method", __call = function(args)
                local typename, field_names = args[1], args[2]
                local fields = {}
                if type(field_names) == "string" then
                    local s = string.gsub(field_names, ",", " ")
                    for w in string.gmatch(s, "%S+") do table.insert(fields, w) end
                elseif type(field_names) == "table" and (field_names.__type == "list" or field_names.__type == "tuple") then
                    for _, f in ipairs(field_names.elements) do table.insert(fields, f) end
                end
                local nt_dict = {
                    __match_args__ = { __type = "tuple", elements = fields },
                    __init__ = { __type = "builtin_method", __call = function(iargs, ikwargs)
                        local self = iargs[1]; local elements = {}
                        for i = 2, #iargs do table.insert(elements, iargs[i]) end
                        if #elements < #fields and ikwargs then
                            for i = #elements+1, #fields do table.insert(elements, ikwargs[fields[i]]) end
                        end
                        if #elements ~= #fields then Runtime.raise("TypeError", "namedtuple expected " .. #fields .. " arguments, got " .. #elements) end
                        self.__dict.data = {__type="tuple", elements=elements}; return nil
                    end},
                    __iter__ = { __type = "builtin_method", __call = function(iargs) return Runtime.builtins.iter({iargs[1].__dict.data}) end},
                    __len__ = { __type = "builtin_method", __call = function(iargs) return #fields end},
                    __repr__ = { __type = "builtin_method", __call = function(iargs)
                        local parts = {}
                        for i, f in ipairs(fields) do table.insert(parts, f .. "=" .. Runtime.builtins.repr({iargs[1].__dict.data.elements[i]})) end
                        return typename .. "(" .. table.concat(parts, ", ") .. ")"
                    end}
                }
                for i, f in ipairs(fields) do
                    nt_dict[f] = { __type = "property", fget = { __type = "builtin_method", __call = function(iargs) return iargs[1].__dict.data.elements[i] end } }
                end
                return PyClass.make_class(typename, {}, nt_dict)
            end }
            return cache.namedtuple
        end
        
        return nil
    end

    -- 建立帶有自訂 __getattr__ 的模組物件
    local mod_dict = {
        __getattr__ = { __type = "builtin_method", __call = function(args)
            local self, a = args[1], args[2]
            local val = get_attr(a)
            if val then
                self.__dict[a] = val
                return val
            end
            Runtime.raise("AttributeError", "module 'collections' has no attribute '" .. tostring(a) .. "'")
        end }
    }
    local mod = PyClass.make_instance(PyClass.make_class("module", {}, mod_dict))
    mod.__dict["__name__"] = "collections"
	mod.__dict.abc = abc_mod
	if module_name == "collections.abc" then
		if type(env_vars) == type({}) then 
			env_vars["collections"] = env_vars["collections"] or mod
		end
		return abc_mod 
	end
    return mod
end

-- 對外接口
function Librarys.has_module(name)
    local root = name:match("^([^%.]+)")
    if name == "collections" or name == "functools" or name == "itertools" then return true end
    return stdlibs[root] ~= nil
end

function Librarys.get_module(name)
    local root, sub = name:match("^([^%.]+)%.?(.*)$")
	if name == "collections" then return Librarys.init_collections_module() end
    if name == "functools" then return init_functools_module() end
    if name == "itertools" then return init_itertools_module() end
    if not stdlibs[root] then Runtime.raise("ImportError", "No module named '" .. root .. "'") end

    
    if sub and sub ~= "" then
        if stdlibs[root][sub] then return make_namespace(name, stdlibs[root][sub]) end
        Runtime.raise("ImportError", "No module named '" .. name .. "'")
    end
    return make_namespace(name, stdlibs[root])
end

return Librarys