io.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #pragma once
  2. #include "ceval.h"
  3. #include "cffi.h"
  4. #include "common.h"
  5. #if PK_ENABLE_OS
  6. #include <fstream>
  7. #include <filesystem>
  8. namespace pkpy{
  9. inline int _ = set_read_file_cwd([](const Str& name){
  10. std::filesystem::path path(name.sv());
  11. bool exists = std::filesystem::exists(path);
  12. if(!exists) return Bytes();
  13. std::ifstream ifs(path, std::ios::binary);
  14. std::vector<char> buffer(std::istreambuf_iterator<char>(ifs), {});
  15. ifs.close();
  16. return Bytes(std::move(buffer));
  17. });
  18. struct FileIO {
  19. PY_CLASS(FileIO, io, FileIO)
  20. Str file;
  21. Str mode;
  22. std::fstream _fs;
  23. bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; }
  24. FileIO(VM* vm, Str file, Str mode): file(file), mode(mode) {
  25. std::ios_base::openmode extra = static_cast<std::ios_base::openmode>(0);
  26. if(mode == "rb" || mode == "wb" || mode == "ab"){
  27. extra |= std::ios::binary;
  28. }
  29. if(mode == "rt" || mode == "r" || mode == "rb"){
  30. _fs.open(file.str(), std::ios::in | extra);
  31. }else if(mode == "wt" || mode == "w" || mode == "wb"){
  32. _fs.open(file.str(), std::ios::out | extra);
  33. }else if(mode == "at" || mode == "a" || mode == "ab"){
  34. _fs.open(file.str(), std::ios::app | extra);
  35. }else{
  36. vm->ValueError("invalid mode");
  37. }
  38. if(!_fs.is_open()) vm->IOError(strerror(errno));
  39. }
  40. static void _register(VM* vm, PyObject* mod, PyObject* type){
  41. vm->bind_static_method<2>(type, "__new__", [](VM* vm, ArgsView args){
  42. return VAR_T(FileIO,
  43. vm, CAST(Str, args[0]), CAST(Str, args[1])
  44. );
  45. });
  46. vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){
  47. FileIO& io = CAST(FileIO&, args[0]);
  48. std::vector<char> buffer;
  49. while(true){
  50. char c = io._fs.get();
  51. if(io._fs.eof()) break;
  52. buffer.push_back(c);
  53. }
  54. Bytes b(std::move(buffer));
  55. if(io.is_text()) return VAR(Str(b.str()));
  56. return VAR(std::move(b));
  57. });
  58. vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){
  59. FileIO& io = CAST(FileIO&, args[0]);
  60. if(io.is_text()) io._fs << CAST(Str&, args[1]);
  61. else{
  62. Bytes& buffer = CAST(Bytes&, args[1]);
  63. io._fs.write(buffer.data(), buffer.size());
  64. }
  65. return vm->None;
  66. });
  67. vm->bind_method<0>(type, "close", [](VM* vm, ArgsView args){
  68. FileIO& io = CAST(FileIO&, args[0]);
  69. io._fs.close();
  70. return vm->None;
  71. });
  72. vm->bind_method<0>(type, "__exit__", [](VM* vm, ArgsView args){
  73. FileIO& io = CAST(FileIO&, args[0]);
  74. io._fs.close();
  75. return vm->None;
  76. });
  77. vm->bind_method<0>(type, "__enter__", CPP_LAMBDA(vm->None));
  78. }
  79. };
  80. inline void add_module_io(VM* vm){
  81. PyObject* mod = vm->new_module("io");
  82. FileIO::register_class(vm, mod);
  83. vm->bind_builtin_func<2>("open", [](VM* vm, ArgsView args){
  84. static StrName m_io("io");
  85. static StrName m_FileIO("FileIO");
  86. return vm->call(vm->_modules[m_io]->attr(m_FileIO), args[0], args[1]);
  87. });
  88. }
  89. inline void add_module_os(VM* vm){
  90. PyObject* mod = vm->new_module("os");
  91. PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
  92. mod->attr().set("path", path_obj);
  93. // Working directory is shared by all VMs!!
  94. vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){
  95. return VAR(std::filesystem::current_path().string());
  96. });
  97. vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){
  98. std::filesystem::path path(CAST(Str&, args[0]).sv());
  99. std::filesystem::current_path(path);
  100. return vm->None;
  101. });
  102. vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){
  103. std::filesystem::path path(CAST(Str&, args[0]).sv());
  104. std::filesystem::directory_iterator di;
  105. try{
  106. di = std::filesystem::directory_iterator(path);
  107. }catch(std::filesystem::filesystem_error& e){
  108. std::string msg = e.what();
  109. auto pos = msg.find_last_of(":");
  110. if(pos != std::string::npos) msg = msg.substr(pos + 1);
  111. vm->IOError(Str(msg).lstrip());
  112. }
  113. List ret;
  114. for(auto& p: di) ret.push_back(VAR(p.path().filename().string()));
  115. return VAR(ret);
  116. });
  117. vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){
  118. std::filesystem::path path(CAST(Str&, args[0]).sv());
  119. bool ok = std::filesystem::remove(path);
  120. if(!ok) vm->IOError("operation failed");
  121. return vm->None;
  122. });
  123. vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){
  124. std::filesystem::path path(CAST(Str&, args[0]).sv());
  125. bool ok = std::filesystem::create_directory(path);
  126. if(!ok) vm->IOError("operation failed");
  127. return vm->None;
  128. });
  129. vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){
  130. std::filesystem::path path(CAST(Str&, args[0]).sv());
  131. bool ok = std::filesystem::remove(path);
  132. if(!ok) vm->IOError("operation failed");
  133. return vm->None;
  134. });
  135. vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){
  136. std::filesystem::path path;
  137. for(int i=0; i<args.size(); i++){
  138. path /= CAST(Str&, args[i]).sv();
  139. }
  140. return VAR(path.string());
  141. });
  142. vm->bind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){
  143. std::filesystem::path path(CAST(Str&, args[0]).sv());
  144. bool exists = std::filesystem::exists(path);
  145. return VAR(exists);
  146. });
  147. vm->bind_func<1>(path_obj, "basename", [](VM* vm, ArgsView args){
  148. std::filesystem::path path(CAST(Str&, args[0]).sv());
  149. return VAR(path.filename().string());
  150. });
  151. }
  152. } // namespace pkpy
  153. #else
  154. namespace pkpy{
  155. inline void add_module_io(void* vm){}
  156. inline void add_module_os(void* vm){}
  157. } // namespace pkpy
  158. #endif