io.h 5.6 KB

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