← Back
Editing: file.hpp
#ifndef PYTHONIC_TYPES_FILE_HPP #define PYTHONIC_TYPES_FILE_HPP #include "pythonic/include/types/file.hpp" #include "pythonic/types/assignable.hpp" #include "pythonic/utils/shared_ref.hpp" #include "pythonic/types/str.hpp" #include "pythonic/types/list.hpp" #include "pythonic/types/NoneType.hpp" #include "pythonic/types/attr.hpp" #include "pythonic/builtins/IOError.hpp" #include "pythonic/builtins/ValueError.hpp" #include "pythonic/builtins/RuntimeError.hpp" #include "pythonic/builtins/StopIteration.hpp" #include <fstream> #include <iterator> #include <cstring> #include <string> #include <cstdio> #include <memory> #ifdef _WIN32 #include <io.h> #else #include <unistd.h> #endif PYTHONIC_NS_BEGIN namespace types { /// _file implementation _file::_file() : f(nullptr) { } // TODO : no check on file existance? _file::_file(types::str const &filename, types::str const &strmode) : f(fopen(filename.c_str(), strmode.c_str())) { } FILE *_file::operator*() const { return f; } _file::~_file() { if (f) fclose(f); } /// file implementation // Constructors file::file() : file_iterator(), data(utils::no_memory()) { } file::file(types::str const &filename, types::str const &strmode) : file_iterator(), data(utils::no_memory()), mode(strmode), name(filename), newlines('\n') { open(filename, strmode); if (mode.find_first_of("r+") != -1) *(file_iterator *)this = file_iterator(*this); } // Iterators file::iterator file::begin() { return *this; } file::iterator file::end() { return {}; } // Modifiers void file::open(types::str const &filename, types::str const &strmode) { const char *smode = strmode.c_str(); // Python enforces that the mode, after stripping 'U', begins with 'r', // 'w' || 'a'. if (*smode == 'U') { ++smode; } // Not implemented yet data = utils::shared_ref<container_type>(filename, smode); if (!**data) throw types::IOError("Couldn't open file " + filename); is_open = true; } void file::close() { fclose(**data); data->f = nullptr; is_open = false; } bool file::closed() const { return !is_open; } types::str const &file::getmode() const { return mode; } types::str const &file::getname() const { return name; } types::str const &file::getnewlines() const { // Python seems to always return none... Doing the same here return newlines; } bool file::eof() { return ::feof(**data); } void file::flush() { if (!is_open) throw ValueError("I/O operation on closed file"); fflush(**data); } long file::fileno() const { if (!is_open) throw ValueError("I/O operation on closed file"); return ::fileno(**data); } bool file::isatty() const { if (!is_open) throw ValueError("I/O operation on closed file"); return ::isatty(this->fileno()); } types::str file::read(long size) { if (!is_open) throw ValueError("I/O operation on closed file"); if (mode.find_first_of("r+") == -1) throw IOError("File not open for reading"); if (size == 0 || (feof(**data) && mode.find_first_of("ra") == -1)) return types::str(); long curr_pos = tell(); seek(0, SEEK_END); size = size < 0 ? tell() - curr_pos : size; seek(curr_pos); std::unique_ptr<char[]> content{new char[size + 1]}; // This part needs a new implementation of types::str(char*) to avoid // unnecessary copy. types::str res(content.get(), fread(content.get(), sizeof(char), size, **data)); return res; } types::str file::readline(long size) { if (!is_open) throw ValueError("I/O operation on closed file"); if (mode.find_first_of("r+") == -1) throw IOError("File not open for reading"); constexpr static long BUFFER_SIZE = 1024; types::str res; char read_str[BUFFER_SIZE]; for (long i = 0; i < size; i += BUFFER_SIZE) { // +1 because we read the last chunk so we don't want to count \0 if (fgets(read_str, std::min(BUFFER_SIZE - 1, size - i) + 1, **data)) res += read_str; if (feof(**data) || res[res.size() - 1] == "\n") break; } return res; } types::list<types::str> file::readlines(long sizehint) { // Official python doc specifies that sizehint is used as a max of chars // But it has not been implemented in the standard python interpreter... types::str str; types::list<types::str> lst(0); while ((str = readline())) lst.push_back(str); return lst; } void file::seek(long offset, long whence) { if (!is_open) throw ValueError("I/O operation on closed file"); if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) throw IOError("file.seek() : Invalid argument."); fseek(**data, offset, whence); } long file::tell() const { if (!is_open) throw ValueError("I/O operation on closed file"); return ftell(**data); } void file::truncate(long size) { if (!is_open) throw ValueError("I/O operation on closed file"); if (mode.find_first_of("wa+") == -1) throw IOError("file.write() : File not open for writing."); if (size < 0) size = this->tell(); #ifdef _WIN32 long error = _chsize_s(fileno(), size); #else long error = ftruncate(fileno(), size); #endif if (error == -1) throw RuntimeError(strerror(errno)); } long file::write(types::str const &str) { if (!is_open) throw ValueError("I/O operation on closed file"); if (mode.find_first_of("wa+") == -1) throw IOError("file.write() : File not open for writing."); return fwrite(str.c_str(), sizeof(char), str.size(), **data); } template <class T> void file::writelines(T const &seq) { auto end = seq.end(); for (auto it = seq.begin(); it != end; ++it) write(*it); } /// file_iterator implementation // TODO : What if the file disappears before the end? // Like in : // for line in open("myfile"): // print line file_iterator::file_iterator(file &ref) : f(&ref), set(false), curr(), position(ref.tell()) { } file_iterator::file_iterator() : f(nullptr), set(false), curr(), position(std::numeric_limits<long>::max()){}; bool file_iterator::operator==(file_iterator const &f2) const { return position == f2.position; } bool file_iterator::operator!=(file_iterator const &f2) const { return position != f2.position; } bool file_iterator::operator<(file_iterator const &f2) const { // Not really elegant... // Equivalent to 'return *this != f2;' return position < f2.position; } file_iterator &file_iterator::operator++() { if (f->eof()) return *this; operator*(); set = false; operator*(); position = f->eof() ? std::numeric_limits<long>::max() : f->tell(); return *this; } types::str file_iterator::operator*() const { if (!set) { curr = f->readline(); set = true; } return curr.chars(); // to make a copy } } PYTHONIC_NS_END /* pythran attribute system { */ PYTHONIC_NS_BEGIN namespace builtins { bool getattr(types::attr::CLOSED, types::file const &f) { return f.closed(); } types::str const &getattr(types::attr::MODE, types::file const &f) { return f.getmode(); } types::str const &getattr(types::attr::NAME, types::file const &f) { return f.getname(); } // Python seems to always return none... Doing the same. types::none_type getattr(types::attr::NEWLINES, types::file const &f) { return builtins::None; } } PYTHONIC_NS_END /* } */ #endif
Save File
Cancel