add requests module

This commit is contained in:
blueloveTH 2023-04-30 21:53:50 +08:00
parent e5d5f09d83
commit a04cdb4cad
6 changed files with 155 additions and 7 deletions

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ src/_generated.h
profile.sh profile.sh
test test
tmp.rar tmp.rar
src/httplib.h

View File

@ -9,7 +9,7 @@ pipeline = [
["common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"], ["common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"],
["obj.h", "codeobject.h", "frame.h"], ["obj.h", "codeobject.h", "frame.h"],
["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"], ["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"],
["iter.h", "cffi.h", "io.h", "_generated.h", "pocketpy.h"] ["iter.h", "cffi.h", "requests.h", "io.h", "_generated.h", "pocketpy.h"]
] ]
copied = set() copied = set()

View File

@ -74,7 +74,7 @@ class dict:
def items(self): def items(self):
for kv in self._a: for kv in self._a:
if kv is not None: if kv is not None:
yield kv yield kv[0], kv[1]
def clear(self): def clear(self):
self._a = [None] * self._capacity self._a = [None] * self._capacity

40
python/requests.py Normal file
View File

@ -0,0 +1,40 @@
class Response:
def __init__(self, status_code, reason, content):
self.status_code = status_code
self.reason = reason
self.content = content
assert type(self.status_code) is int
assert type(self.reason) is str
assert type(self.content) is bytes
@property
def text(self):
return self.content.decode()
def __repr__(self):
code = self.status_code
return f'<Response [{code}]>'
def _parse_h(headers):
if headers is None:
return []
if type(headers) is dict:
return list(headers.items())
raise ValueError('headers must be dict or None')
def get(url, headers=None):
headers = _parse_h(headers)
return _request('GET', url, headers, None)
def post(url, data: bytes, headers=None):
headers = _parse_h(headers)
return _request('POST', url, headers, data)
def put(url, data: bytes, headers=None):
headers = _parse_h(headers)
return _request('PUT', url, headers, data)
def delete(url, headers=None):
headers = _parse_h(headers)
return _request('DELETE', url, headers, None)

View File

@ -6,6 +6,7 @@
#include "repl.h" #include "repl.h"
#include "iter.h" #include "iter.h"
#include "cffi.h" #include "cffi.h"
#include "requests.h"
#include "io.h" #include "io.h"
#include "_generated.h" #include "_generated.h"
@ -920,11 +921,6 @@ inline void VM::post_init(){
add_module_gc(this); add_module_gc(this);
add_module_random(this); add_module_random(this);
if(enable_os){
add_module_io(this);
add_module_os(this);
}
for(const char* name: {"this", "functools", "collections", "heapq", "bisect"}){ for(const char* name: {"this", "functools", "collections", "heapq", "bisect"}){
_lazy_modules[name] = kPythonLibs[name]; _lazy_modules[name] = kPythonLibs[name];
} }
@ -969,6 +965,12 @@ inline void VM::post_init(){
} }
return VAR(MappingProxy(args[0])); return VAR(MappingProxy(args[0]));
})); }));
if(enable_os){
add_module_io(this);
add_module_os(this);
add_module_requests(this);
}
#endif #endif
} }

105
src/requests.h Normal file
View File

@ -0,0 +1,105 @@
#pragma once
#include "common.h"
#include "obj.h"
#include "vm.h"
#include "_generated.h"
#if __has_include("httplib.h")
#include "httplib.h"
namespace pkpy {
inline void add_module_requests(VM* vm){
static StrName m_requests("requests");
static StrName m_Response("Response");
PyObject* mod = vm->new_module(m_requests);
CodeObject_ code = vm->compile(kPythonLibs["requests"], "requests.py", EXEC_MODE);
vm->_exec(code, mod);
vm->bind_func<4>(mod, "_request", [](VM* vm, ArgsView args){
Str method = CAST(Str&, args[0]);
Str url = CAST(Str&, args[1]);
PyObject* headers = args[2]; // a dict object
PyObject* body = args[3]; // a bytes object
if(url.index("http://") != 0){
vm->ValueError("url must start with http://");
}
for(char c: url){
switch(c){
case '.':
case '-':
case '_':
case '~':
case ':':
case '/':
break;
default:
if(!isalnum(c)){
vm->ValueError(fmt("invalid character in url: '", c, "'"));
}
}
}
int slash = url.index("/", 7);
Str path = "/";
if(slash != -1){
path = url.substr(slash);
url = url.substr(0, slash);
if(path.empty()) path = "/";
}
httplib::Client client(url.str());
httplib::Headers h;
if(headers != vm->None){
List list = CAST(List&, headers);
for(auto& item : list){
Tuple t = CAST(Tuple&, item);
Str key = CAST(Str&, t[0]);
Str value = CAST(Str&, t[1]);
h.emplace(key.str(), value.str());
}
}
auto _to_resp = [=](const httplib::Result& res){
std::vector<char> buf(res->body.size());
for(int i=0; i<res->body.size(); i++) buf[i] = res->body[i];
return vm->call(
vm->_modules[m_requests]->attr(m_Response),
VAR(res->status),
VAR(res->reason),
VAR(Bytes(std::move(buf)))
);
};
if(method == "GET"){
httplib::Result res = client.Get(path.str(), h);
return _to_resp(res);
}else if(method == "POST"){
Bytes b = CAST(Bytes&, body);
httplib::Result res = client.Post(path.str(), h, b.data(), b.size(), "application/octet-stream");
return _to_resp(res);
}else if(method == "PUT"){
Bytes b = CAST(Bytes&, body);
httplib::Result res = client.Put(path.str(), h, b.data(), b.size(), "application/octet-stream");
return _to_resp(res);
}else if(method == "DELETE"){
httplib::Result res = client.Delete(path.str(), h);
return _to_resp(res);
}else{
vm->ValueError("invalid method");
}
UNREACHABLE();
});
}
} // namespace pkpy
#else
inline void add_module_requests(VM* vm){ }
#endif