From 3d12c9400cb00831061d822a5920e0b887ed5999 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 18 Jan 2025 22:12:44 +0800 Subject: [PATCH] add `libhv` module --- .gitignore | 2 + .gitmodules | 3 + 3rd/libhv/CMakeLists.txt | 37 +++ 3rd/libhv/compile_flags.txt | 15 ++ 3rd/libhv/include/libhv_bindings.hpp | 10 + 3rd/libhv/src/HttpClient.cpp | 305 +++++++++++++++++++++++++ 3rd/libhv/src/HttpServer.cpp | 7 + 3rd/libhv/src/WebSocketClient.cpp | 7 + 3rd/libhv/src/WebSocketServer.cpp | 7 + 3rd/libhv/src/libhv_bindings.cpp | 10 + CMakeLists.txt | 13 +- build_g_32.sh | 2 +- cmake_build.py | 2 +- compile_flags.txt | 3 +- include/pocketpy/common/utils.h | 6 - include/pocketpy/interpreter/modules.h | 6 + include/pocketpy/pocketpy.h | 2 + include/typings/libhv.pyi | 34 +++ src/interpreter/vm.c | 3 +- src/public/py_str.c | 10 + 20 files changed, 473 insertions(+), 11 deletions(-) create mode 100644 .gitmodules create mode 100644 3rd/libhv/CMakeLists.txt create mode 100644 3rd/libhv/compile_flags.txt create mode 100644 3rd/libhv/include/libhv_bindings.hpp create mode 100644 3rd/libhv/src/HttpClient.cpp create mode 100644 3rd/libhv/src/HttpServer.cpp create mode 100644 3rd/libhv/src/WebSocketClient.cpp create mode 100644 3rd/libhv/src/WebSocketServer.cpp create mode 100644 3rd/libhv/src/libhv_bindings.cpp create mode 100644 include/typings/libhv.pyi diff --git a/.gitignore b/.gitignore index efd5fa98..b37883a4 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ docs/references.md tests/00_tmp.py docs/C-API/functions.md + +*.log diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..583a9da0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rd/libhv/libhv"] + path = 3rd/libhv/libhv + url = https://github.com/ithewei/libhv diff --git a/3rd/libhv/CMakeLists.txt b/3rd/libhv/CMakeLists.txt new file mode 100644 index 00000000..4e321d9e --- /dev/null +++ b/3rd/libhv/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.10) + +project(libhv_bindings) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +option(BUILD_SHARED "build shared library" OFF) +option(BUILD_STATIC "build static library" ON) +option(BUILD_EXAMPLES "build examples" OFF) +option(WITH_OPENSSL "with openssl library" ON) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/libhv) + +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_LIST_DIR}/src LIBHV_BINDINGS_SRC) + +add_library(${PROJECT_NAME} STATIC ${LIBHV_BINDINGS_SRC}) + +target_link_libraries(${PROJECT_NAME} hv_static) + +# define WITHOUT_HTTP_CONTENT +target_compile_definitions(libhv_bindings PRIVATE WITHOUT_HTTP_CONTENT) +target_compile_definitions(hv_static PRIVATE WITHOUT_HTTP_CONTENT) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/../../include + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/libhv + ${CMAKE_CURRENT_LIST_DIR}/libhv/base + ${CMAKE_CURRENT_LIST_DIR}/libhv/evpp + ${CMAKE_CURRENT_LIST_DIR}/libhv/event + ${CMAKE_CURRENT_LIST_DIR}/libhv/http + ${CMAKE_CURRENT_LIST_DIR}/libhv/ssl + ${CMAKE_CURRENT_LIST_DIR}/libhv/cpputil +) \ No newline at end of file diff --git a/3rd/libhv/compile_flags.txt b/3rd/libhv/compile_flags.txt new file mode 100644 index 00000000..01d80705 --- /dev/null +++ b/3rd/libhv/compile_flags.txt @@ -0,0 +1,15 @@ +-xc++ +-std=c++14 + +-I../../include + +-Iinclude/ +-Ilibhv/ +-Ilibhv/base/ +-Ilibhv/evpp/ +-Ilibhv/event/ +-Ilibhv/http/ +-Ilibhv/ssl/ +-Ilibhv/cpputil/ + +-DWITHOUT_HTTP_CONTENT \ No newline at end of file diff --git a/3rd/libhv/include/libhv_bindings.hpp b/3rd/libhv/include/libhv_bindings.hpp new file mode 100644 index 00000000..d37c260f --- /dev/null +++ b/3rd/libhv/include/libhv_bindings.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "pocketpy.h" + +extern "C" void pk__add_module_libhv(); + +py_Type libhv_register_HttpClient(py_GlobalRef mod); +py_Type libhv_register_HttpServer(py_GlobalRef mod); +py_Type libhv_register_WebSocketClient(py_GlobalRef mod); +py_Type libhv_register_WebSocketServer(py_GlobalRef mod); diff --git a/3rd/libhv/src/HttpClient.cpp b/3rd/libhv/src/HttpClient.cpp new file mode 100644 index 00000000..10276387 --- /dev/null +++ b/3rd/libhv/src/HttpClient.cpp @@ -0,0 +1,305 @@ +#include "libhv_bindings.hpp" +#include "base/herr.h" +#include "http/client/HttpClient.h" + +struct libhv_HttpResponse { + HttpResponsePtr ptr; + bool ok; + + bool is_valid() { return ok && ptr != NULL; } + + libhv_HttpResponse() : ptr(NULL), ok(false) {} +}; + +static bool libhv_HttpResponse_status_code(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); + py_newint(py_retval(), resp->ptr->status_code); + return true; +}; + +static bool libhv_HttpResponse_headers(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); + py_Ref headers = py_getslot(argv, 0); + if(py_isnil(headers)) { + py_newdict(headers); + py_Ref _0 = py_pushtmp(); + py_Ref _1 = py_pushtmp(); + for(auto& kv: resp->ptr->headers) { + py_newstr(_0, kv.first.c_str()); + py_newstr(_1, kv.second.c_str()); + py_dict_setitem(headers, _0, _1); + } + py_shrink(2); + } + py_assign(py_retval(), headers); + return true; +}; + +static bool libhv_HttpResponse_text(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); + py_Ref text = py_getslot(argv, 1); + if(py_isnil(text)) { + c11_sv sv; + sv.data = resp->ptr->body.c_str(); + sv.size = resp->ptr->body.size(); + py_newstrv(text, sv); + } + py_assign(py_retval(), text); + return true; +}; + +static bool libhv_HttpResponse_content(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); + py_Ref content = py_getslot(argv, 2); + if(py_isnil(content)) { + int size = resp->ptr->body.size(); + unsigned char* buf = py_newbytes(content, size); + memcpy(buf, resp->ptr->body.data(), size); + } + py_assign(py_retval(), content); + return true; +}; + +static bool libhv_HttpResponse_json(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); + const char* source = resp->ptr->body.c_str(); // json string is null-terminated + return py_json_loads(source); +}; + +static bool libhv_HttpResponse_completed(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + py_newbool(py_retval(), resp->is_valid()); + return true; +} + +static bool libhv_HttpResponse__new__(int argc, py_Ref argv) { + return py_exception(tp_NotImplementedError, ""); +} + +static bool libhv_HttpResponse__iter__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + py_assign(py_retval(), argv); + return true; +} + +static bool libhv_HttpResponse__next__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + if(!resp->is_valid()) { + py_newnone(py_retval()); + return true; + } else { + if(!py_tpcall(tp_StopIteration, 1, argv)) return false; + return py_raise(py_retval()); + } +} + +static bool libhv_HttpResponse__repr__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); + if(!resp->is_valid()) { + py_newstr(py_retval(), ""); + } else { + py_newfstr(py_retval(), "", (int)resp->ptr->status_code); + } + return true; +} + +static py_Type libhv_register_HttpResponse(py_GlobalRef mod) { + py_Type type = py_newtype("HttpResponse", tp_object, mod, [](void* ud) { + ((libhv_HttpResponse*)ud)->~libhv_HttpResponse(); + }); + + py_bindproperty(type, "status_code", libhv_HttpResponse_status_code, NULL); + py_bindproperty(type, "headers", libhv_HttpResponse_headers, NULL); + py_bindproperty(type, "text", libhv_HttpResponse_text, NULL); + py_bindproperty(type, "content", libhv_HttpResponse_content, NULL); + py_bindmethod(type, "json", libhv_HttpResponse_json); + + py_bindmagic(type, __new__, libhv_HttpResponse__new__); + py_bindmagic(type, __iter__, libhv_HttpResponse__iter__); + py_bindmagic(type, __next__, libhv_HttpResponse__next__); + py_bindmagic(type, __repr__, libhv_HttpResponse__repr__); + // completed + py_bindproperty(type, "completed", libhv_HttpResponse_completed, NULL); + return type; +} + +static bool libhv_HttpClient__send_request(py_Ref arg_self, + enum http_method method, + py_Ref arg_url, + py_Ref arg_params, + py_Ref arg_headers, + py_Ref arg_data, + py_Ref arg_json, + py_Ref arg_timeout) { + hv::HttpClient* cli = (hv::HttpClient*)py_touserdata(arg_self); + if(!py_checkstr(arg_url)) return false; + const char* url = py_tostr(arg_url); + if(!py_checkint(arg_timeout)) return false; + int timeout = py_toint(arg_timeout); + + auto req = std::make_shared(); + req->method = method; + req->url = url; + if(!py_isnone(arg_params)) { + if(!py_checktype(arg_params, tp_dict)) return false; + + bool ok = py_dict_apply( + arg_params, + [](py_Ref key, py_Ref value, void* ctx) { + HttpRequest* p_req = (HttpRequest*)ctx; + if(!py_checkstr(key)) return false; + if(!py_str(value)) return false; // key: str(value) + p_req->SetParam(py_tostr(key), py_tostr(py_retval())); + return true; + }, + req.get()); + if(!ok) return false; + } + + req->headers["Connection"] = "keep-alive"; + + if(!py_isnone(arg_headers)) { + if(!py_checktype(arg_headers, tp_dict)) return false; + + bool ok = py_dict_apply( + arg_headers, + [](py_Ref key, py_Ref value, void* ctx) { + HttpRequest* p_req = (HttpRequest*)ctx; + if(!py_checkstr(key)) return false; + if(!py_str(value)) return false; // key: str(value) + p_req->headers[py_tostr(key)] = py_tostr(py_retval()); + return true; + }, + req.get()); + if(!ok) return false; + } + + if(!py_isnone(arg_data)) { + // data must be str or bytes + if(py_istype(arg_data, tp_str)) { + req->body = py_tostr(arg_data); + } else if(py_istype(arg_data, tp_bytes)) { + int size; + unsigned char* buf = py_tobytes(arg_data, &size); + req->body.assign((const char*)buf, size); + } else { + return TypeError("HttpClient: data must be str or bytes"); + } + } + + if(!py_isnone(arg_json)) { + if(!py_isnone(arg_data)) { + return ValueError("HttpClient: data and json cannot be set at the same time"); + } + + if(!py_json_dumps(arg_json)) return false; + req->body = py_tostr(py_retval()); + req->headers["Content-Type"] = "application/json"; + } + + req->timeout = timeout; + + libhv_HttpResponse* retval = + (libhv_HttpResponse*)py_newobject(py_retval(), + py_gettype("libhv", py_name("HttpResponse")), + 3, // headers, text, content + sizeof(libhv_HttpResponse)); + // placement new + new (retval) libhv_HttpResponse(); + + int code = cli->sendAsync(req, [retval](const HttpResponsePtr& resp) { + if(resp == NULL) { + retval->ok = false; + retval->ptr = NULL; + } else { + retval->ok = true; + retval->ptr = resp; + } + }); + if(code != 0) { + const char* msg = hv_strerror(code); + return RuntimeError("HttpClient: %s (%d)", msg, code); + } + return true; +} + +py_Type libhv_register_HttpClient(py_GlobalRef mod) { + py_Type type = py_newtype("HttpClient", tp_object, mod, [](void* ud) { + ((hv::HttpClient*)ud)->~HttpClient(); + }); + py_GlobalRef type_object = py_tpobject(type); + libhv_register_HttpResponse(mod); + + py_bindmagic(type, __new__, [](int argc, py_Ref argv) { + hv::HttpClient* ud = + (hv::HttpClient*)py_newobject(py_retval(), py_totype(argv), 0, sizeof(hv::HttpClient)); + new (ud) hv::HttpClient(); + return true; + }); + + py_bind(type_object, + "get(self, url: str, params=None, headers=None, timeout=10)", + [](int argc, py_Ref argv) { + return libhv_HttpClient__send_request(py_arg(0), // self + HTTP_GET, // method + py_arg(1), // url + py_arg(2), // params + py_arg(3), // headers + py_None(), // data + py_None(), // json + py_arg(4)); // timeout + }); + + py_bind(type_object, + "post(self, url: str, params=None, headers=None, data=None, json=None, timeout=10)", + [](int argc, py_Ref argv) { + return libhv_HttpClient__send_request(py_arg(0), // self + HTTP_POST, // method + py_arg(1), // url + py_arg(2), // params + py_arg(3), // headers + py_arg(4), // data + py_arg(5), // json + py_arg(6)); // timeout + }); + + py_bind(type_object, + "put(self, url: str, params=None, headers=None, data=None, json=None, timeout=10)", + [](int argc, py_Ref argv) { + return libhv_HttpClient__send_request(py_arg(0), // self + HTTP_PUT, // method + py_arg(1), // url + py_arg(2), // params + py_arg(3), // headers + py_arg(4), // data + py_arg(5), // json + py_arg(6)); // timeout + }); + + py_bind(type_object, + "delete(self, url: str, params=None, headers=None, timeout=10)", + [](int argc, py_Ref argv) { + return libhv_HttpClient__send_request(py_arg(0), // self + HTTP_DELETE, // method + py_arg(1), // url + py_arg(2), // params + py_arg(3), // headers + py_None(), // data + py_None(), // json + py_arg(4)); // timeout + }); + return type; +} diff --git a/3rd/libhv/src/HttpServer.cpp b/3rd/libhv/src/HttpServer.cpp new file mode 100644 index 00000000..0a79bd41 --- /dev/null +++ b/3rd/libhv/src/HttpServer.cpp @@ -0,0 +1,7 @@ +#include "libhv_bindings.hpp" +#include "http/server/HttpServer.h" + +py_Type libhv_register_HttpServer(py_GlobalRef mod){ + py_Type type = py_newtype("HttpServer", tp_object, mod, NULL); + return type; +} \ No newline at end of file diff --git a/3rd/libhv/src/WebSocketClient.cpp b/3rd/libhv/src/WebSocketClient.cpp new file mode 100644 index 00000000..2d9e27dc --- /dev/null +++ b/3rd/libhv/src/WebSocketClient.cpp @@ -0,0 +1,7 @@ +#include "libhv_bindings.hpp" +#include "http/client/WebSocketClient.h" + +py_Type libhv_register_WebSocketClient(py_GlobalRef mod) { + py_Type type = py_newtype("WebSocketClient", tp_object, mod, NULL); + return type; +} \ No newline at end of file diff --git a/3rd/libhv/src/WebSocketServer.cpp b/3rd/libhv/src/WebSocketServer.cpp new file mode 100644 index 00000000..43dd677d --- /dev/null +++ b/3rd/libhv/src/WebSocketServer.cpp @@ -0,0 +1,7 @@ +#include "libhv_bindings.hpp" +#include "http/server/WebSocketServer.h" + +py_Type libhv_register_WebSocketServer(py_GlobalRef mod) { + py_Type type = py_newtype("WebSocketServer", tp_object, mod, NULL); + return type; +} diff --git a/3rd/libhv/src/libhv_bindings.cpp b/3rd/libhv/src/libhv_bindings.cpp new file mode 100644 index 00000000..6ade67a8 --- /dev/null +++ b/3rd/libhv/src/libhv_bindings.cpp @@ -0,0 +1,10 @@ +#include "libhv_bindings.hpp" + +extern "C" void pk__add_module_libhv() { + py_GlobalRef mod = py_newmodule("libhv"); + + libhv_register_HttpClient(mod); + libhv_register_HttpServer(mod); + libhv_register_WebSocketClient(mod); + libhv_register_WebSocketServer(mod); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index d93a36e1..5d4324ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,14 @@ option(PK_BUILD_MODULE_LZ4 "" ON) if(PK_BUILD_MODULE_LZ4) add_subdirectory(3rd/lz4) include_directories(3rd/lz4) - add_definitions(-DPK_BUILD_MODULE_LZ4=1) + add_definitions(-DPK_BUILD_MODULE_LZ4) +endif() + +option(PK_BUILD_MODULE_LIBHV "" OFF) +if(PK_BUILD_MODULE_LIBHV) + add_subdirectory(3rd/libhv) + include_directories(3rd/libhv/include) + add_definitions(-DPK_BUILD_MODULE_LIBHV) endif() # PK_IS_MAIN determines whether the project is being used from root @@ -97,3 +104,7 @@ endif() if(PK_BUILD_MODULE_LZ4) target_link_libraries(${PROJECT_NAME} lz4) endif() + +if(PK_BUILD_MODULE_LIBHV) + target_link_libraries(${PROJECT_NAME} libhv_bindings) +endif() \ No newline at end of file diff --git a/build_g_32.sh b/build_g_32.sh index 02694296..2e34f145 100644 --- a/build_g_32.sh +++ b/build_g_32.sh @@ -4,7 +4,7 @@ python prebuild.py SRC=$(find src/ -name "*.c") -FLAGS="-std=c11 -lm -ldl -I3rd/lz4 -Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1 -DPK_BUILD_MODULE_LZ4=1" +FLAGS="-std=c11 -lm -ldl -I3rd/lz4 -Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1 -DPK_BUILD_MODULE_LZ4" SANITIZE_FLAGS="-fsanitize=address,leak,undefined" diff --git a/cmake_build.py b/cmake_build.py index c0636544..7c662159 100644 --- a/cmake_build.py +++ b/cmake_build.py @@ -20,7 +20,7 @@ assert config in ['Debug', 'Release', 'RelWithDebInfo'] os.chdir("build") -code = os.system(f"cmake .. -DPK_ENABLE_OS=ON -DCMAKE_BUILD_TYPE={config} {extra_flags}") +code = os.system(f"cmake .. -DPK_ENABLE_OS=ON -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_LIBHV=ON -DCMAKE_BUILD_TYPE={config} {extra_flags}") assert code == 0 code = os.system(f"cmake --build . --config {config}") assert code == 0 diff --git a/compile_flags.txt b/compile_flags.txt index 02155d39..d0397504 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -3,4 +3,5 @@ -xc -std=c11 -Iinclude/ --I3rd/lz4/ \ No newline at end of file +-I3rd/lz4/ +-I3rd/libhv/include/ \ No newline at end of file diff --git a/include/pocketpy/common/utils.h b/include/pocketpy/common/utils.h index b4a158e0..8610e0f3 100644 --- a/include/pocketpy/common/utils.h +++ b/include/pocketpy/common/utils.h @@ -48,9 +48,3 @@ typedef struct RefCounted { } \ } while(0) -// static assert -#ifndef __cplusplus - #ifndef static_assert - #define static_assert(x, msg) if(!(x)) c11__abort("static_assert failed: %s", msg) - #endif -#endif diff --git a/include/pocketpy/interpreter/modules.h b/include/pocketpy/interpreter/modules.h index 5681316d..58618671 100644 --- a/include/pocketpy/interpreter/modules.h +++ b/include/pocketpy/interpreter/modules.h @@ -24,3 +24,9 @@ void pk__add_module_colorcvt(); void pk__add_module_conio(); void pk__add_module_lz4(); void pk__add_module_pkpy(); + +#ifdef PK_BUILD_MODULE_LIBHV +void pk__add_module_libhv(); +#else +#define pk__add_module_libhv() +#endif \ No newline at end of file diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index d4dc2bb6..d66ba6d4 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -159,6 +159,8 @@ PK_API void py_newstr(py_OutRef, const char*); PK_API char* py_newstrn(py_OutRef, int); /// Create a `str` object from a `c11_sv`. PK_API void py_newstrv(py_OutRef, c11_sv); +/// Create a formatted `str` object. +PK_API void py_newfstr(py_OutRef, const char*, ...); /// Create a `bytes` object with `n` UNINITIALIZED bytes. PK_API unsigned char* py_newbytes(py_OutRef, int n); /// Create a `None` object. diff --git a/include/typings/libhv.pyi b/include/typings/libhv.pyi new file mode 100644 index 00000000..9fdcb8ed --- /dev/null +++ b/include/typings/libhv.pyi @@ -0,0 +1,34 @@ +from typing import Literal, Generator + +class Future[T]: + def completed(self) -> bool: ... + def __iter__(self) -> Generator[T, None, None]: ... + +class HttpResponse(Future['HttpResponse']): + @property + def status_code(self) -> int: ... + @property + def headers(self) -> dict[str, str]: ... + @property + def text(self) -> str: ... + @property + def content(self) -> bytes: ... + + def json(self): ... + + +class HttpClient: + def get(self, url: str, params=None, headers=None, timeout=10) -> HttpResponse: ... + def post(self, url: str, params=None, headers=None, data=None, json=None, timeout=10) -> HttpResponse: ... + def put(self, url: str, params=None, headers=None, data=None, json=None, timeout=10) -> HttpResponse: ... + def delete(self, url: str, params=None, headers=None, timeout=10) -> HttpResponse: ... + + +class HttpServer: + pass + +class WebSocketClient: + pass + +class WebSocketServer: + pass \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 8b815c26..5d5deb6d 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -221,7 +221,8 @@ void VM__ctor(VM* self) { pk__add_module_importlib(); pk__add_module_conio(); - pk__add_module_lz4(); + pk__add_module_lz4(); // optional + pk__add_module_libhv(); // optional pk__add_module_pkpy(); // add python builtins diff --git a/src/public/py_str.c b/src/public/py_str.c index b5018938..4b6bd43d 100644 --- a/src/public/py_str.c +++ b/src/public/py_str.c @@ -25,6 +25,16 @@ void py_newstrv(py_OutRef out, c11_sv sv) { memcpy(data, sv.data, sv.size); } +void py_newfstr(py_OutRef out, const char* fmt, ...) { + c11_sbuf buf; + c11_sbuf__ctor(&buf); + va_list args; + va_start(args, fmt); + pk_vsprintf(&buf, fmt, args); + va_end(args); + c11_sbuf__py_submit(&buf, out); +} + unsigned char* py_newbytes(py_Ref out, int size) { ManagedHeap* heap = &pk_current_vm->heap; // 4 bytes size + data