Merge branch 'main' into c_binding_api

This commit is contained in:
Kolten Pearson 2023-04-30 08:50:28 -06:00
commit 6bc1749b5c
9 changed files with 168 additions and 21 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"] ["_generated.h", "iter.h", "cffi.h", "requests.h", "io.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

@ -11,7 +11,7 @@
namespace pkpy{ namespace pkpy{
inline Bytes _read_file_cwd(const Str& name){ inline int _ = set_read_file_cwd([](const Str& name){
std::filesystem::path path(name.sv()); std::filesystem::path path(name.sv());
bool exists = std::filesystem::exists(path); bool exists = std::filesystem::exists(path);
if(!exists) return Bytes(); if(!exists) return Bytes();
@ -19,7 +19,7 @@ inline Bytes _read_file_cwd(const Str& name){
std::vector<char> buffer(std::istreambuf_iterator<char>(ifs), {}); std::vector<char> buffer(std::istreambuf_iterator<char>(ifs), {});
ifs.close(); ifs.close();
return Bytes(std::move(buffer)); return Bytes(std::move(buffer));
} });
struct FileIO { struct FileIO {
PY_CLASS(FileIO, io, FileIO) PY_CLASS(FileIO, io, FileIO)
@ -182,13 +182,8 @@ inline void add_module_os(VM* vm){
#else #else
namespace pkpy{ namespace pkpy{
inline void add_module_io(VM* vm){} inline void add_module_io(void* vm){}
inline void add_module_os(VM* vm){} inline void add_module_os(void* vm){}
inline Bytes _read_file_cwd(const Str& name){
return Bytes();
}
} // namespace pkpy } // namespace pkpy
#endif #endif

View File

@ -12,19 +12,22 @@ struct Function;
class VM; class VM;
typedef PyObject* (*NativeFuncC)(VM*, ArgsView); typedef PyObject* (*NativeFuncC)(VM*, ArgsView);
typedef shared_ptr<CodeObject> CodeObject_; typedef int (*LuaStyleFuncC)(VM*);
struct NativeFunc { struct NativeFunc {
NativeFuncC f; NativeFuncC f;
int argc; // DONOT include self int argc; // DONOT include self
bool method; bool method;
// this is designed for lua style C bindings
// access it via `CAST(NativeFunc&, args[-2])._lua_f`
LuaStyleFuncC _lua_f;
NativeFunc(NativeFuncC f, int argc, bool method) : f(f), argc(argc), method(method) {} NativeFunc(NativeFuncC f, int argc, bool method) : f(f), argc(argc), method(method), _lua_f(nullptr) {}
PyObject* operator()(VM* vm, ArgsView args) const; PyObject* operator()(VM* vm, ArgsView args) const;
}; };
typedef shared_ptr<CodeObject> CodeObject_;
typedef void (*StackFunc)(VM*);
struct FuncDecl { struct FuncDecl {
struct KwArg { struct KwArg {
@ -79,6 +82,7 @@ struct Bytes{
Bytes() : _data(), _ok(false) {} Bytes() : _data(), _ok(false) {}
Bytes(std::vector<char>&& data) : _data(std::move(data)), _ok(true) {} Bytes(std::vector<char>&& data) : _data(std::move(data)), _ok(true) {}
Bytes(const std::string& data) : _data(data.begin(), data.end()), _ok(true) {}
operator bool() const noexcept { return _ok; } operator bool() const noexcept { return _ok; }
}; };

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
} }

103
src/requests.h Normal file
View File

@ -0,0 +1,103 @@
#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){
return vm->call(
vm->_modules[m_requests]->attr(m_Response),
VAR(res->status),
VAR(res->reason),
VAR(Bytes(res->body))
);
};
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(void* vm){ }
#endif

View File

@ -24,7 +24,9 @@ namespace pkpy{
#define POPX() (s_data.popx()) #define POPX() (s_data.popx())
#define STACK_VIEW(n) (s_data.view(n)) #define STACK_VIEW(n) (s_data.view(n))
Bytes _read_file_cwd(const Str& name); typedef Bytes (*ReadFileCwdFunc)(const Str& name);
inline ReadFileCwdFunc _read_file_cwd = [](const Str& name) { return Bytes(); };
inline int set_read_file_cwd(ReadFileCwdFunc func) { _read_file_cwd = func; return 0; }
#define DEF_NATIVE_2(ctype, ptype) \ #define DEF_NATIVE_2(ctype, ptype) \
template<> inline ctype py_cast<ctype>(VM* vm, PyObject* obj) { \ template<> inline ctype py_cast<ctype>(VM* vm, PyObject* obj) { \