Commit 550e52ed authored by Marius Wachtler's avatar Marius Wachtler

switch to cpythons fileobject implementation

fixes test_file2k, test_file_eintr and test_softspace
parent 8cf264f2
......@@ -89,6 +89,7 @@ file(GLOB_RECURSE STDOBJECT_SRCS Objects
dictproxy.c
exceptions.c
floatobject.c
fileobject.c
iterobject.c
memoryobject.c
stringobject.c
......
......@@ -8,8 +8,6 @@
extern "C" {
#endif
// Pyston change: this is not our format
#if 0
typedef struct {
PyObject_HEAD
FILE *f_fp;
......@@ -34,15 +32,8 @@ typedef struct {
int readable;
int writable;
} PyFileObject;
#endif
typedef struct _PyFileObject PyFileObject;
// Pyston change: use this to access the fp instead of ->f_fp
PyAPI_FUNC(void) PyFile_SetFP(PyObject*, FILE*) PYSTON_NOEXCEPT;
// Pyston change: this is no longer a static object
PyAPI_DATA(PyTypeObject*) file_cls;
#define PyFile_Type (*file_cls)
PyAPI_DATA(PyTypeObject) PyFile_Type;
#define PyFile_Check(op) PyObject_TypeCheck(op, &PyFile_Type)
#define PyFile_CheckExact(op) (Py_TYPE(op) == &PyFile_Type)
......@@ -63,9 +54,6 @@ PyAPI_FUNC(int) PyFile_SoftSpace(PyObject *, int) PYSTON_NOEXCEPT;
PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *) PYSTON_NOEXCEPT;
PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *) PYSTON_NOEXCEPT;
// Pyston temporary addition:
PyAPI_FUNC(PyObject *) PyFile_GetEncoding(PyObject *) PYSTON_NOEXCEPT;
/* The default encoding used by the platform file system APIs
If non-NULL, this is different than the default encoding for strings
*/
......
# expected: fail
import sys
import os
import unittest
......@@ -30,6 +29,8 @@ class AutoFileTests(unittest.TestCase):
self.f.close()
os.remove(TESTFN)
# Pyston change: disabled
@unittest.skip("this depends on refcounting")
def testWeakRefs(self):
# verify weak references
p = proxy(self.f)
......
# expected: fail
# Written to test interrupted system calls interfering with our many buffered
# IO implementations. http://bugs.python.org/issue12268
#
......
# expected: fail
from test.test_support import run_unittest
import unittest
import StringIO
......
......@@ -89,6 +89,6 @@ AttrwrapperType = type(_C().__dict__)
GetSetDescriptorType = type(FunctionType.func_code)
# Pyston change:
# MemberDescriptorType = type(FunctionType.func_globals)
MemberDescriptorType = type(file.softspace)
MemberDescriptorType = type(type.__dict__["__flags__"])
del sys, _f, _g, _C, _x # Not for export
......@@ -6914,11 +6914,7 @@ posix_fdopen(PyObject *self, PyObject *args)
if (fp == NULL)
return posix_error();
/* We now know we will succeed, so initialize the file object. */
// Pyston change:
PyFile_SetFP(f, fp);
//((PyFileObject *)f)->f_fp = fp;
((PyFileObject *)f)->f_fp = fp;
PyFile_SetBufSize(f, bufsize);
return f;
}
......
// This file is originally from CPython 2.7, with modifications for Pyston
/* File object implementation */
#define PY_SSIZE_T_CLEAN
......@@ -138,7 +140,7 @@ dircheck(PyFileObject* f)
static PyObject *
fill_file_fields(PyFileObject *f, FILE *fp, PyObject *name, char *mode,
fill_file_fields(PyFileObject *f, FILE *fp, PyObject *name, const char *mode,
int (*close)(FILE *))
{
assert(name != NULL);
......@@ -427,7 +429,9 @@ close_the_file(PyFileObject *f)
if (local_fp != NULL) {
local_close = f->f_close;
if (local_close != NULL && f->unlocked_count > 0) {
if (f->ob_refcnt > 0) {
// Pyston change:
// if (f->ob_refcnt > 0) {
if (/*f->ob_refcnt*/ 2 > 0) {
PyErr_SetString(PyExc_IOError,
"close() called during concurrent "
"operation on the same file object.");
......@@ -466,7 +470,7 @@ close_the_file(PyFileObject *f)
}
PyObject *
PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *))
PyFile_FromFile(FILE *fp, const char *name, const char *mode, int (*close)(FILE *))
{
PyFileObject *f;
PyObject *o_name;
......@@ -1596,7 +1600,9 @@ PyFile_GetLine(PyObject *f, int n)
"EOF when reading a line");
}
else if (s[len-1] == '\n') {
if (result->ob_refcnt == 1) {
// Pyston change:
// if (result->ob_refcnt == 1) {
if (/*result->ob_refcnt*/ 2 == 1) {
if (_PyString_Resize(&result, len-1))
return NULL;
}
......@@ -1619,7 +1625,9 @@ PyFile_GetLine(PyObject *f, int n)
"EOF when reading a line");
}
else if (s[len-1] == '\n') {
if (result->ob_refcnt == 1)
// Pyston change:
// if (result->ob_refcnt == 1)
if (/*result->ob_refcnt*/ 2 == 1)
PyUnicode_Resize(&result, len-1);
else {
PyObject *v;
......@@ -2466,7 +2474,9 @@ PyDoc_STR(
);
PyTypeObject PyFile_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
// Pyston change:
// PyVarObject_HEAD_INIT(&PyType_Type, 0)
PyVarObject_HEAD_INIT(NULL, 0)
"file",
sizeof(PyFileObject),
0,
......
......@@ -783,7 +783,7 @@ tok_stdin_decode(struct tok_state *tok, char **inp)
if (sysstdin == NULL || !PyFile_Check(sysstdin))
return 0;
enc = PyFile_GetEncoding(sysstdin);
enc = ((PyFileObject*)sysstdin)->f_encoding;
if (enc == NULL || !PyString_Check(enc))
return 0;
Py_INCREF(enc);
......
......@@ -91,7 +91,6 @@ add_library(PYSTON_OBJECTS OBJECT ${OPTIONAL_SRCS}
runtime/descr.cpp
runtime/dict.cpp
runtime/exceptions.cpp
runtime/file.cpp
runtime/float.cpp
runtime/frame.cpp
runtime/generator.cpp
......
......@@ -179,8 +179,6 @@ void initGlobalFuncs(GlobalState& g) {
g.funcs.allowGLReadPreemption = getFunc((void*)threading::allowGLReadPreemption, "allowGLReadPreemption");
GET(softspace);
GET(createFunctionFromMetadata);
GET(getFunctionMetadata);
GET(createUserClass);
......
......@@ -27,7 +27,6 @@
#include "core/ast.h"
#include "core/types.h"
#include "runtime/classobj.h"
#include "runtime/file.h"
#include "runtime/ics.h"
#include "runtime/import.h"
#include "runtime/inline/list.h"
......@@ -309,7 +308,7 @@ Box* open(Box* arg1, Box* arg2, Box* arg3) {
assert(arg2);
assert(arg3);
// This could be optimized quite a bit if it ends up being important:
return runtimeCall(file_cls, ArgPassSpec(3), arg1, arg2, arg3, NULL, NULL);
return runtimeCall(&PyFile_Type, ArgPassSpec(3), arg1, arg2, arg3, NULL, NULL);
}
extern "C" Box* chr(Box* arg) {
......@@ -2015,7 +2014,7 @@ void setupBuiltins() {
builtins_module->giveAttr("xrange", xrange_cls);
open_obj = new BoxedBuiltinFunctionOrMethod(
FunctionMetadata::create((void*)open, typeFromClass(file_cls), 3, false, false,
FunctionMetadata::create((void*)open, typeFromClass(&PyFile_Type), 3, false, false,
ParamNames({ "name", "mode", "buffering" }, "", "")),
"open", { boxString("r"), boxInt(-1) }, NULL, open_doc);
builtins_module->giveAttr("open", open_obj);
......@@ -2078,7 +2077,7 @@ void setupBuiltins() {
builtins_module->giveAttr("list", list_cls);
builtins_module->giveAttr("slice", slice_cls);
builtins_module->giveAttr("type", type_cls);
builtins_module->giveAttr("file", file_cls);
builtins_module->giveAttr("file", &PyFile_Type);
builtins_module->giveAttr("bool", bool_cls);
builtins_module->giveAttr("dict", dict_cls);
builtins_module->giveAttr("set", set_cls);
......
......@@ -26,7 +26,6 @@
#include "capi/types.h"
#include "codegen/unwinding.h"
#include "core/types.h"
#include "runtime/file.h"
#include "runtime/inline/boxing.h"
#include "runtime/inline/list.h"
#include "runtime/int.h"
......@@ -101,7 +100,8 @@ BoxedList* getSysPath() {
Box* getSysStdout() {
Box* sys_stdout = sys_module->getattr(internStringMortal("stdout"));
RELEASE_ASSERT(sys_stdout, "lost sys.stdout??");
if (!sys_stdout)
raiseExcHelper(RuntimeError, "lost sys.stdout");
return sys_stdout;
}
......@@ -653,13 +653,6 @@ void setupSys() {
sys_module->giveAttr("argv", new BoxedList());
sys_module->giveAttr("stdout", new BoxedFile(stdout, "<stdout>", "w"));
sys_module->giveAttr("stdin", new BoxedFile(stdin, "<stdin>", "r"));
sys_module->giveAttr("stderr", new BoxedFile(stderr, "<stderr>", "w"));
sys_module->giveAttr("__stdout__", sys_module->getattr(internStringMortal("stdout")));
sys_module->giveAttr("__stdin__", sys_module->getattr(internStringMortal("stdin")));
sys_module->giveAttr("__stderr__", sys_module->getattr(internStringMortal("stderr")));
sys_module->giveAttr("exc_info",
new BoxedBuiltinFunctionOrMethod(FunctionMetadata::create((void*)sysExcInfo, BOXED_TUPLE, 0),
"exc_info", exc_info_doc));
......
......@@ -40,7 +40,6 @@
#include "core/threading.h"
#include "core/types.h"
#include "runtime/classobj.h"
#include "runtime/file.h"
#include "runtime/import.h"
#include "runtime/objmodel.h"
#include "runtime/rewrite_args.h"
......@@ -933,7 +932,7 @@ extern "C" PyObject* PyExceptionInstance_Class(PyObject* o) noexcept {
}
extern "C" int PyTraceBack_Print(PyObject* v, PyObject* f) noexcept {
RELEASE_ASSERT(f->cls == file_cls && static_cast<BoxedFile*>(f)->f_fp == stderr,
RELEASE_ASSERT(f->cls == &PyFile_Type && ((PyFileObject*)f)->f_fp == stderr,
"sorry will only print tracebacks to stderr right now");
printTraceback(v);
return 0;
......
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "runtime/file.h"
#include <cstdio>
#include <cstring>
#include <sstream>
#include "capi/types.h"
#include "core/common.h"
#include "core/stats.h"
#include "core/types.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
#include "runtime/util.h"
namespace pyston {
#define BUF(v) PyString_AS_STRING((PyStringObject*)v)
#ifdef HAVE_GETC_UNLOCKED
#define GETC(f) getc_unlocked(f)
#define FLOCKFILE(f) flockfile(f)
#define FUNLOCKFILE(f) funlockfile(f)
#else
#define GETC(f) getc(f)
#define FLOCKFILE(f)
#define FUNLOCKFILE(f)
#endif
/* Bits in f_newlinetypes */
#define NEWLINE_UNKNOWN 0 /* No newline seen, yet */
#define NEWLINE_CR 1 /* \r newline seen */
#define NEWLINE_LF 2 /* \n newline seen */
#define NEWLINE_CRLF 4 /* \r\n newline seen */
#define FILE_BEGIN_ALLOW_THREADS(fobj) \
{ \
fobj->unlocked_count++; \
Py_BEGIN_ALLOW_THREADS
#define FILE_END_ALLOW_THREADS(fobj) \
Py_END_ALLOW_THREADS fobj->unlocked_count--; \
assert(fobj->unlocked_count >= 0); \
}
#define FILE_ABORT_ALLOW_THREADS(fobj) \
Py_BLOCK_THREADS fobj->unlocked_count--; \
assert(fobj->unlocked_count >= 0);
#if BUFSIZ < 8192
#define SMALLCHUNK 8192
#else
#define SMALLCHUNK BUFSIZ
#endif
static size_t new_buffersize(BoxedFile* f, size_t currentsize) {
#ifdef HAVE_FSTAT
off_t pos, end;
struct stat st;
if (fstat(fileno(f->f_fp), &st) == 0) {
end = st.st_size;
/* The following is not a bug: we really need to call lseek()
*and* ftell(). The reason is that some stdio libraries
mistakenly flush their buffer when ftell() is called and
the lseek() call it makes fails, thereby throwing away
data that cannot be recovered in any way. To avoid this,
we first test lseek(), and only call ftell() if lseek()
works. We can't use the lseek() value either, because we
need to take the amount of buffered data into account.
(Yet another reason why stdio stinks. :-) */
pos = lseek(fileno(f->f_fp), 0L, SEEK_CUR);
if (pos >= 0) {
pos = ftell(f->f_fp);
}
if (pos < 0)
clearerr(f->f_fp);
if (end > pos && pos >= 0)
return currentsize + end - pos + 1;
/* Add 1 so if the file were to grow we'd notice. */
}
#endif
/* Expand the buffer by an amount proportional to the current size,
giving us amortized linear-time behavior. Use a less-than-double
growth factor to avoid excessive allocation. */
return currentsize + (currentsize >> 3) + 6;
}
#if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN
#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK || (x) == EAGAIN)
#else
#ifdef EWOULDBLOCK
#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK)
#else
#ifdef EAGAIN
#define BLOCKED_ERRNO(x) ((x) == EAGAIN)
#else
#define BLOCKED_ERRNO(x) 0
#endif
#endif
#endif
static PyObject* err_closed(void) noexcept {
PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
return NULL;
}
static PyObject* err_mode(const char* action) noexcept {
PyErr_Format(PyExc_IOError, "File not open for %s", action);
return NULL;
}
/* Refuse regular file I/O if there's data in the iteration-buffer.
* Mixing them would cause data to arrive out of order, as the read*
* methods don't use the iteration buffer. */
static PyObject* err_iterbuffered(void) noexcept {
PyErr_SetString(PyExc_ValueError, "Mixing iteration and read methods would lose data");
return NULL;
}
/*
** Py_UniversalNewlineFgets is an fgets variation that understands
** all of \r, \n and \r\n conventions.
** The stream should be opened in binary mode.
** If fobj is NULL the routine always does newline conversion, and
** it may peek one char ahead to gobble the second char in \r\n.
** If fobj is non-NULL it must be a PyFileObject. In this case there
** is no readahead but in stead a flag is used to skip a following
** \n on the next read. Also, if the file is open in binary mode
** the whole conversion is skipped. Finally, the routine keeps track of
** the different types of newlines seen.
** Note that we need no error handling: fgets() treats error and eof
** identically.
*/
extern "C" char* Py_UniversalNewlineFgets(char* buf, int n, FILE* stream, PyObject* fobj) noexcept {
char* p = buf;
int c;
int newlinetypes = 0;
int skipnextlf = 0;
int univ_newline = 1;
if (fobj) {
if (!PyFile_Check(fobj)) {
errno = ENXIO; /* What can you do... */
return NULL;
}
univ_newline = ((BoxedFile*)fobj)->f_univ_newline;
if (!univ_newline)
return fgets(buf, n, stream);
newlinetypes = ((BoxedFile*)fobj)->f_newlinetypes;
skipnextlf = ((BoxedFile*)fobj)->f_skipnextlf;
}
FLOCKFILE(stream);
c = 'x'; /* Shut up gcc warning */
while (--n > 0 && (c = GETC(stream)) != EOF) {
if (skipnextlf) {
skipnextlf = 0;
if (c == '\n') {
/* Seeing a \n here with skipnextlf true
** means we saw a \r before.
*/
newlinetypes |= NEWLINE_CRLF;
c = GETC(stream);
if (c == EOF)
break;
} else {
/*
** Note that c == EOF also brings us here,
** so we're okay if the last char in the file
** is a CR.
*/
newlinetypes |= NEWLINE_CR;
}
}
if (c == '\r') {
/* A \r is translated into a \n, and we skip
** an adjacent \n, if any. We don't set the
** newlinetypes flag until we've seen the next char.
*/
skipnextlf = 1;
c = '\n';
} else if (c == '\n') {
newlinetypes |= NEWLINE_LF;
}
*p++ = c;
if (c == '\n')
break;
}
if (c == EOF && skipnextlf)
newlinetypes |= NEWLINE_CR;
FUNLOCKFILE(stream);
*p = '\0';
if (fobj) {
((BoxedFile*)fobj)->f_newlinetypes = newlinetypes;
((BoxedFile*)fobj)->f_skipnextlf = skipnextlf;
} else if (skipnextlf) {
/* If we have no file object we cannot save the
** skipnextlf flag. We have to readahead, which
** will cause a pause if we're reading from an
** interactive stream, but that is very unlikely
** unless we're doing something silly like
** execfile("/dev/tty").
*/
c = GETC(stream);
if (c != '\n')
ungetc(c, stream);
}
if (p == buf)
return NULL;
return buf;
}
static BoxedFile* dircheck(BoxedFile* f) {
#if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR)
struct stat buf;
if (f->f_fp == NULL)
return f;
if (fstat(fileno(f->f_fp), &buf) == 0 && S_ISDIR(buf.st_mode)) {
char* msg = strerror(EISDIR);
PyObject* exc = PyObject_CallFunction(PyExc_IOError, "(isO)", EISDIR, msg, f->f_name);
PyErr_SetObject(PyExc_IOError, exc);
Py_XDECREF(exc);
return NULL;
}
#endif
return f;
}
static PyObject* fill_file_fields(BoxedFile* f, FILE* fp, PyObject* name, const char* mode, int (*close)(FILE*)) {
assert(name != NULL);
assert(f != NULL);
assert(PyFile_Check(f));
assert(f->f_fp == NULL);
Py_DECREF(f->f_name);
Py_DECREF(f->f_mode);
Py_DECREF(f->f_encoding);
Py_DECREF(f->f_errors);
Py_INCREF(name);
f->f_name = name;
f->f_mode = PyString_FromString(mode);
f->f_close = close;
f->f_softspace = 0;
f->f_binary = strchr(mode, 'b') != NULL;
f->f_buf = NULL;
f->f_univ_newline = (strchr(mode, 'U') != NULL);
f->f_newlinetypes = NEWLINE_UNKNOWN;
f->f_skipnextlf = 0;
Py_INCREF(Py_None);
f->f_encoding = Py_None;
Py_INCREF(Py_None);
f->f_errors = Py_None;
f->readable = f->writable = 0;
if (strchr(mode, 'r') != NULL || f->f_univ_newline)
f->readable = 1;
if (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL)
f->writable = 1;
if (strchr(mode, '+') != NULL)
f->readable = f->writable = 1;
if (f->f_mode == NULL)
return NULL;
f->f_fp = fp;
f = dircheck(f);
return (PyObject*)f;
}
BoxedFile::BoxedFile(FILE* f, std::string fname, const char* fmode, int (*close)(FILE*))
// Zero out fields not set by fill_file_fields:
: f_fp(NULL),
f_bufend(NULL),
f_bufptr(0),
f_setbuf(0),
unlocked_count(0) {
Box* r = fill_file_fields(this, f, boxString(fname), fmode, close);
checkAndThrowCAPIException();
assert(r == this);
}
Box* fileRepr(BoxedFile* self) {
assert(self->cls == file_cls);
void* addr = static_cast<void*>(self->f_fp);
std::ostringstream repr;
repr << "<" << (self->f_fp ? "open" : "closed") << " file '" << PyString_AsString(self->f_name) << "', ";
repr << "mode '" << PyString_AsString(self->f_mode) << "' at " << addr << ">";
return boxString(repr.str());
}
static void checkOpen(BoxedFile* self) {
if (!self->f_fp)
raiseExcHelper(IOError, "I/O operation on closed file");
}
static void checkReadable(BoxedFile* self) {
checkOpen(self);
if (!self->readable)
raiseExcHelper(IOError, "File not open for reading");
}
static void checkWritable(BoxedFile* self) {
checkOpen(self);
if (!self->writable)
raiseExcHelper(IOError, "File not open for writing");
}
static PyObject* file_read(BoxedFile* f, long bytesrequested) noexcept {
size_t bytesread, buffersize, chunksize;
PyObject* v;
if (f->f_fp == NULL)
return err_closed();
if (!f->readable)
return err_mode("reading");
/* refuse to mix with f.next() */
if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && f->f_buf[0] != '\0')
return err_iterbuffered();
if (bytesrequested < 0)
buffersize = new_buffersize(f, (size_t)0);
else
buffersize = bytesrequested;
if (buffersize > PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError, "requested number of bytes is more than a Python string can hold");
return NULL;
}
v = PyString_FromStringAndSize((char*)NULL, buffersize);
if (v == NULL)
return NULL;
bytesread = 0;
for (;;) {
int interrupted;
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
chunksize = Py_UniversalNewlineFread(BUF(v) + bytesread, buffersize - bytesread, f->f_fp, (PyObject*)f);
interrupted = ferror(f->f_fp) && errno == EINTR;
FILE_END_ALLOW_THREADS(f)
if (interrupted) {
clearerr(f->f_fp);
if (PyErr_CheckSignals()) {
Py_DECREF(v);
return NULL;
}
}
if (chunksize == 0) {
if (interrupted)
continue;
if (!ferror(f->f_fp))
break;
clearerr(f->f_fp);
/* When in non-blocking mode, data shouldn't
* be discarded if a blocking signal was
* received. That will also happen if
* chunksize != 0, but bytesread < buffersize. */
if (bytesread > 0 && BLOCKED_ERRNO(errno))
break;
PyErr_SetFromErrno(PyExc_IOError);
Py_DECREF(v);
return NULL;
}
bytesread += chunksize;
if (bytesread < buffersize && !interrupted) {
clearerr(f->f_fp);
break;
}
if (bytesrequested < 0) {
buffersize = new_buffersize(f, buffersize);
if (_PyString_Resize(&v, buffersize) < 0)
return NULL;
} else {
/* Got what was requested. */
break;
}
}
if (bytesread != buffersize && _PyString_Resize(&v, bytesread))
return NULL;
return v;
}
static PyObject* get_line(BoxedFile* f, int n) noexcept {
FILE* fp = f->f_fp;
int c;
char* buf, *end;
size_t total_v_size; /* total # of slots in buffer */
size_t used_v_size; /* # used slots in buffer */
size_t increment; /* amount to increment the buffer */
PyObject* v;
int newlinetypes = f->f_newlinetypes;
int skipnextlf = f->f_skipnextlf;
int univ_newline = f->f_univ_newline;
#if defined(USE_FGETS_IN_GETLINE)
if (n <= 0 && !univ_newline)
return getline_via_fgets(f, fp);
#endif
total_v_size = n > 0 ? n : 100;
v = PyString_FromStringAndSize((char*)NULL, total_v_size);
if (v == NULL)
return NULL;
buf = BUF(v);
end = buf + total_v_size;
for (;;) {
FILE_BEGIN_ALLOW_THREADS(f)
FLOCKFILE(fp);
if (univ_newline) {
c = 'x'; /* Shut up gcc warning */
while (buf != end && (c = GETC(fp)) != EOF) {
if (skipnextlf) {
skipnextlf = 0;
if (c == '\n') {
/* Seeing a \n here with
* skipnextlf true means we
* saw a \r before.
*/
newlinetypes |= NEWLINE_CRLF;
c = GETC(fp);
if (c == EOF)
break;
} else {
newlinetypes |= NEWLINE_CR;
}
}
if (c == '\r') {
skipnextlf = 1;
c = '\n';
} else if (c == '\n')
newlinetypes |= NEWLINE_LF;
*buf++ = c;
if (c == '\n')
break;
}
if (c == EOF) {
if (ferror(fp) && errno == EINTR) {
FUNLOCKFILE(fp);
FILE_ABORT_ALLOW_THREADS(f)
f->f_newlinetypes = newlinetypes;
f->f_skipnextlf = skipnextlf;
if (PyErr_CheckSignals()) {
Py_DECREF(v);
return NULL;
}
/* We executed Python signal handlers and got no exception.
* Now back to reading the line where we left off. */
clearerr(fp);
continue;
}
if (skipnextlf)
newlinetypes |= NEWLINE_CR;
}
} else /* If not universal newlines use the normal loop */
while ((c = GETC(fp)) != EOF && (*buf++ = c) != '\n' && buf != end)
;
FUNLOCKFILE(fp);
FILE_END_ALLOW_THREADS(f)
f->f_newlinetypes = newlinetypes;
f->f_skipnextlf = skipnextlf;
if (c == '\n')
break;
if (c == EOF) {
if (ferror(fp)) {
if (errno == EINTR) {
if (PyErr_CheckSignals()) {
Py_DECREF(v);
return NULL;
}
/* We executed Python signal handlers and got no exception.
* Now back to reading the line where we left off. */
clearerr(fp);
continue;
}
PyErr_SetFromErrno(PyExc_IOError);
clearerr(fp);
Py_DECREF(v);
return NULL;
}
clearerr(fp);
if (PyErr_CheckSignals()) {
Py_DECREF(v);
return NULL;
}
break;
}
/* Must be because buf == end */
if (n > 0)
break;
used_v_size = total_v_size;
increment = total_v_size >> 2; /* mild exponential growth */
total_v_size += increment;
if (total_v_size > PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError, "line is longer than a Python string can hold");
Py_DECREF(v);
return NULL;
}
if (_PyString_Resize(&v, total_v_size) < 0)
return NULL;
buf = BUF(v) + used_v_size;
end = BUF(v) + total_v_size;
}
used_v_size = buf - BUF(v);
if (used_v_size != total_v_size && _PyString_Resize(&v, used_v_size))
return NULL;
return v;
}
Box* fileRead(BoxedFile* self, Box* _size) {
assert(self->cls == file_cls);
if (_size->cls != int_cls) {
raiseExcHelper(TypeError, "an integer is required");
}
int64_t size = static_cast<BoxedInt*>(_size)->n;
Box* r = file_read(self, size);
if (!r)
throwCAPIException();
return r;
}
static PyObject* file_readline(BoxedFile* f, int n = -1) noexcept {
if (f->f_fp == NULL)
return err_closed();
if (!f->readable)
return err_mode("reading");
/* refuse to mix with f.next() */
if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && f->f_buf[0] != '\0')
return err_iterbuffered();
if (n == 0)
return PyString_FromString("");
if (n < 0)
n = 0;
return get_line(f, n);
}
Box* fileReadline1(BoxedFile* self) {
assert(self->cls == file_cls);
Box* r = file_readline(self);
if (!r)
throwCAPIException();
return r;
}
static PyObject* file_write(BoxedFile* f, Box* arg) noexcept {
Py_buffer pbuf;
const char* s;
Py_ssize_t n, n2;
PyObject* encoded = NULL;
int err_flag = 0, err;
if (f->f_fp == NULL)
return err_closed();
if (!f->writable)
return err_mode("writing");
if (f->f_binary) {
// NOTE: this call will create a new tuple every time we write to a binary file. if/when this becomes hot or
// creates too much GC pressure, we can fix it by adding a Pyston specific versino of PyArg_ParseTuple that
// (instead of taking a tuple) takes length + Box**. Then we'd call that directly here (passing "1, &arg").
if (!PyArg_ParseTuple(BoxedTuple::create({ arg }), "s*", &pbuf))
return NULL;
s = (const char*)pbuf.buf;
n = pbuf.len;
} else {
PyObject* text = arg;
if (PyString_Check(text)) {
s = PyString_AS_STRING(text);
n = PyString_GET_SIZE(text);
#ifdef Py_USING_UNICODE
} else if (PyUnicode_Check(text)) {
const char* encoding, *errors;
if (f->f_encoding != Py_None)
encoding = PyString_AS_STRING(f->f_encoding);
else
encoding = PyUnicode_GetDefaultEncoding();
if (f->f_errors != Py_None)
errors = PyString_AS_STRING(f->f_errors);
else
errors = "strict";
encoded = PyUnicode_AsEncodedString(text, encoding, errors);
if (encoded == NULL)
return NULL;
s = PyString_AS_STRING(encoded);
n = PyString_GET_SIZE(encoded);
#endif
} else {
if (PyObject_AsCharBuffer(text, &s, &n))
return NULL;
}
}
// TODO: this doesn't seem like it should be a necessary Pyston change:
// f->f_softspace = 0;
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
n2 = fwrite(s, 1, n, f->f_fp);
if (n2 != n || ferror(f->f_fp)) {
err_flag = 1;
err = errno;
}
FILE_END_ALLOW_THREADS(f)
Py_XDECREF(encoded);
if (f->f_binary)
PyBuffer_Release(&pbuf);
if (err_flag) {
errno = err;
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject* file_writelines(BoxedFile* f, PyObject* seq) noexcept {
#define CHUNKSIZE 1000
PyObject* list, *line;
PyObject* it; /* iter(seq) */
PyObject* result;
int index, islist;
Py_ssize_t i, j, nwritten, len;
assert(seq != NULL);
if (f->f_fp == NULL)
return err_closed();
if (!f->writable)
return err_mode("writing");
result = NULL;
list = NULL;
islist = PyList_Check(seq);
if (islist)
it = NULL;
else {
it = PyObject_GetIter(seq);
if (it == NULL) {
PyErr_SetString(PyExc_TypeError, "writelines() requires an iterable argument");
return NULL;
}
/* From here on, fail by going to error, to reclaim "it". */
list = PyList_New(CHUNKSIZE);
if (list == NULL)
goto error;
}
/* Strategy: slurp CHUNKSIZE lines into a private list,
checking that they are all strings, then write that list
without holding the interpreter lock, then come back for more. */
for (index = 0;; index += CHUNKSIZE) {
if (islist) {
Py_XDECREF(list);
list = PyList_GetSlice(seq, index, index + CHUNKSIZE);
if (list == NULL)
goto error;
j = PyList_GET_SIZE(list);
} else {
for (j = 0; j < CHUNKSIZE; j++) {
line = PyIter_Next(it);
if (line == NULL) {
if (PyErr_Occurred())
goto error;
break;
}
PyList_SetItem(list, j, line);
}
/* The iterator might have closed the file on us. */
if (f->f_fp == NULL) {
err_closed();
goto error;
}
}
if (j == 0)
break;
/* Check that all entries are indeed strings. If not,
apply the same rules as for file.write() and
convert the results to strings. This is slow, but
seems to be the only way since all conversion APIs
could potentially execute Python code. */
for (i = 0; i < j; i++) {
PyObject* v = PyList_GET_ITEM(list, i);
if (!PyString_Check(v)) {
const char* buffer;
int res;
if (f->f_binary) {
res = PyObject_AsReadBuffer(v, (const void**)&buffer, &len);
} else {
res = PyObject_AsCharBuffer(v, &buffer, &len);
}
if (res) {
PyErr_SetString(PyExc_TypeError, "writelines() argument must be a sequence of strings");
goto error;
}
line = PyString_FromStringAndSize(buffer, len);
if (line == NULL)
goto error;
Py_DECREF(v);
PyList_SET_ITEM(list, i, line);
}
}
/* Since we are releasing the global lock, the
following code may *not* execute Python code. */
f->f_softspace = 0;
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
for (i = 0; i < j; i++) {
line = PyList_GET_ITEM(list, i);
len = PyString_GET_SIZE(line);
nwritten = fwrite(PyString_AS_STRING(line), 1, len, f->f_fp);
if (nwritten != len) {
FILE_ABORT_ALLOW_THREADS(f)
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
goto error;
}
}
FILE_END_ALLOW_THREADS(f)
if (j < CHUNKSIZE)
break;
}
Py_INCREF(Py_None);
result = Py_None;
error:
Py_XDECREF(list);
Py_XDECREF(it);
return result;
#undef CHUNKSIZE
}
Box* fileWrite(BoxedFile* self, Box* val) {
assert(self->cls == file_cls);
Box* r = file_write(self, val);
if (!r)
throwCAPIException();
return r;
}
static PyObject* file_flush(BoxedFile* f) noexcept {
int res;
if (f->f_fp == NULL)
return err_closed();
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
res = fflush(f->f_fp);
FILE_END_ALLOW_THREADS(f)
if (res != 0) {
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
Box* fileFlush(BoxedFile* self) {
RELEASE_ASSERT(self->cls == file_cls, "");
Box* r = file_flush(self);
if (!r)
throwCAPIException();
return r;
}
static PyObject* close_the_file(BoxedFile* f) {
int sts = 0;
int (*local_close)(FILE*);
FILE* local_fp = f->f_fp;
char* local_setbuf = f->f_setbuf;
if (local_fp != NULL) {
local_close = f->f_close;
if (local_close != NULL && f->unlocked_count > 0) {
PyErr_SetString(PyExc_IOError, "close() called during concurrent "
"operation on the same file object.");
return NULL;
}
/* NULL out the FILE pointer before releasing the GIL, because
* it will not be valid anymore after the close() function is
* called. */
f->f_fp = NULL;
if (local_close != NULL) {
/* Issue #9295: must temporarily reset f_setbuf so that another
thread doesn't free it when running file_close() concurrently.
Otherwise this close() will crash when flushing the buffer. */
f->f_setbuf = NULL;
Py_BEGIN_ALLOW_THREADS errno = 0;
sts = (*local_close)(local_fp);
Py_END_ALLOW_THREADS f->f_setbuf = local_setbuf;
if (sts == EOF)
return PyErr_SetFromErrno(PyExc_IOError);
if (sts != 0)
return PyInt_FromLong((long)sts);
}
}
Py_RETURN_NONE;
}
/* Our very own off_t-like type, 64-bit if possible */
#if !defined(HAVE_LARGEFILE_SUPPORT)
typedef off_t Py_off_t;
#elif SIZEOF_OFF_T >= 8
typedef off_t Py_off_t;
#elif SIZEOF_FPOS_T >= 8
typedef fpos_t Py_off_t;
#else
#error "Large file support, but neither off_t nor fpos_t is large enough."
#endif
/* a portable fseek() function
return 0 on success, non-zero on failure (with errno set) */
static int _portable_fseek(FILE* fp, Py_off_t offset, int whence) {
#if !defined(HAVE_LARGEFILE_SUPPORT)
return fseek(fp, offset, whence);
#elif defined(HAVE_FSEEKO) && SIZEOF_OFF_T >= 8
return fseeko(fp, offset, whence);
#elif defined(HAVE_FSEEK64)
return fseek64(fp, offset, whence);
#elif defined(__BEOS__)
return _fseek(fp, offset, whence);
#elif SIZEOF_FPOS_T >= 8
/* lacking a 64-bit capable fseek(), use a 64-bit capable fsetpos()
and fgetpos() to implement fseek()*/
fpos_t pos;
switch (whence) {
case SEEK_END:
#ifdef MS_WINDOWS
fflush(fp);
if (_lseeki64(fileno(fp), 0, 2) == -1)
return -1;
#else
if (fseek(fp, 0, SEEK_END) != 0)
return -1;
#endif
/* fall through */
case SEEK_CUR:
if (fgetpos(fp, &pos) != 0)
return -1;
offset += pos;
break;
/* case SEEK_SET: break; */
}
return fsetpos(fp, &offset);
#else
#error "Large file support, but no way to fseek."
#endif
}
static void drop_readahead(BoxedFile* f) {
if (f->f_buf != NULL) {
PyMem_Free(f->f_buf);
f->f_buf = NULL;
}
}
static PyObject* file_seek(BoxedFile* f, PyObject* args) {
int whence;
int ret;
Py_off_t offset;
PyObject* offobj, *off_index;
if (f->f_fp == NULL)
return err_closed();
drop_readahead(f);
whence = 0;
if (!PyArg_ParseTuple(args, "O|i:seek", &offobj, &whence))
return NULL;
off_index = PyNumber_Index(offobj);
if (!off_index) {
if (!PyFloat_Check(offobj))
return NULL;
/* Deprecated in 2.6 */
PyErr_Clear();
if (PyErr_WarnEx(PyExc_DeprecationWarning, "integer argument expected, got float", 1) < 0)
return NULL;
off_index = offobj;
Py_INCREF(offobj);
}
#if !defined(HAVE_LARGEFILE_SUPPORT)
offset = PyInt_AsLong(off_index);
#else
offset = PyLong_Check(off_index) ? PyLong_AsLongLong(off_index) : PyInt_AsLong(off_index);
#endif
Py_DECREF(off_index);
if (PyErr_Occurred())
return NULL;
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
ret = _portable_fseek(f->f_fp, offset, whence);
FILE_END_ALLOW_THREADS(f)
if (ret != 0) {
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
return NULL;
}
f->f_skipnextlf = 0;
Py_INCREF(Py_None);
return Py_None;
}
/* a portable ftell() function
Return -1 on failure with errno set appropriately, current file
position on success */
static Py_off_t _portable_ftell(FILE* fp) {
#if !defined(HAVE_LARGEFILE_SUPPORT)
return ftell(fp);
#elif defined(HAVE_FTELLO) && SIZEOF_OFF_T >= 8
return ftello(fp);
#elif defined(HAVE_FTELL64)
return ftell64(fp);
#elif SIZEOF_FPOS_T >= 8
fpos_t pos;
if (fgetpos(fp, &pos) != 0)
return -1;
return pos;
#else
#error "Large file support, but no way to ftell."
#endif
}
static PyObject* file_tell(BoxedFile* f) {
Py_off_t pos;
if (f->f_fp == NULL)
return err_closed();
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
pos = _portable_ftell(f->f_fp);
FILE_END_ALLOW_THREADS(f)
if (pos == -1) {
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
return NULL;
}
if (f->f_skipnextlf) {
int c;
c = GETC(f->f_fp);
if (c == '\n') {
f->f_newlinetypes |= NEWLINE_CRLF;
pos++;
f->f_skipnextlf = 0;
} else if (c != EOF)
ungetc(c, f->f_fp);
}
#if !defined(HAVE_LARGEFILE_SUPPORT)
return PyInt_FromLong(pos);
#else
return PyLong_FromLongLong(pos);
#endif
}
Box* fileTell(BoxedFile* f) {
if (!isSubclass(f->cls, file_cls))
raiseExcHelper(TypeError, "descriptor 'tell' requires a 'file' object but received a '%s'", getTypeName(f));
auto rtn = file_tell(f);
checkAndThrowCAPIException();
return rtn;
}
Box* fileClose(BoxedFile* self) {
assert(self->cls == file_cls);
PyObject* sts = close_the_file(self);
if (sts) {
PyMem_Free(self->f_setbuf);
self->f_setbuf = NULL;
} else {
throwCAPIException();
}
return sts;
}
Box* fileFileno(BoxedFile* self) {
assert(self->cls == file_cls);
if (!self->f_fp)
raiseExcHelper(IOError, "file is closed");
return boxInt(fileno(self->f_fp));
}
Box* fileEnter(BoxedFile* self) {
assert(self->cls == file_cls);
return self;
}
Box* fileExit(BoxedFile* self, Box* exc_type, Box* exc_val, Box** args) {
Box* exc_tb = args[0];
assert(self->cls == file_cls);
fileClose(self);
return None;
}
// This differs very significantly from CPython:
Box* fileNew(BoxedClass* cls, Box* s, Box* m, Box** args) {
BoxedInt* buffering = (BoxedInt*)args[0];
assert(cls == file_cls);
if (s->cls == unicode_cls) {
s = _PyUnicode_AsDefaultEncodedString(s, NULL);
if (!s)
throwCAPIException();
}
if (m->cls == unicode_cls) {
m = _PyUnicode_AsDefaultEncodedString(m, NULL);
if (!m)
throwCAPIException();
}
if (s->cls != str_cls) {
raiseExcHelper(TypeError, "coercing to Unicode: need string of buffer, %s found", getTypeName(s));
}
if (m->cls != str_cls) {
raiseExcHelper(TypeError, "coercing to Unicode: need string of buffer, %s found", getTypeName(m));
}
if (!PyInt_Check(buffering))
raiseExcHelper(TypeError, "an integer is required");
auto fn = static_cast<BoxedString*>(s);
auto mode = static_cast<BoxedString*>(m);
// all characters in python mode specifiers are valid in fopen calls except 'U'. we strip it out
// of the string we pass to fopen, but pass it along to the BoxedFile ctor.
auto file_mode = std::unique_ptr<char[]>(new char[mode->size() + 1]);
memmove(&file_mode[0], mode->data(), mode->size() + 1);
_PyFile_SanitizeMode(&file_mode[0]);
checkAndThrowCAPIException();
FILE* f = fopen(fn->data(), &file_mode[0]);
if (!f) {
PyErr_SetFromErrnoWithFilename(IOError, fn->data());
throwCAPIException();
abort(); // unreachable;
}
auto file = new BoxedFile(f, fn->s(), PyString_AsString(m));
PyFile_SetBufSize(file, buffering->n);
return file;
}
static PyObject* file_readlines(BoxedFile* f, PyObject* args) noexcept {
long sizehint = 0;
PyObject* list = NULL;
PyObject* line;
char small_buffer[SMALLCHUNK];
char* buffer = small_buffer;
size_t buffersize = SMALLCHUNK;
PyObject* big_buffer = NULL;
size_t nfilled = 0;
size_t nread;
size_t totalread = 0;
char* p, *q, *end;
int err;
int shortread = 0; /* bool, did the previous read come up short? */
if (f->f_fp == NULL)
return err_closed();
if (!f->readable)
return err_mode("reading");
/* refuse to mix with f.next() */
if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && f->f_buf[0] != '\0')
return err_iterbuffered();
if (!PyArg_ParseTuple(args, "|l:readlines", &sizehint))
return NULL;
if ((list = PyList_New(0)) == NULL)
return NULL;
for (;;) {
if (shortread)
nread = 0;
else {
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
nread = Py_UniversalNewlineFread(buffer + nfilled, buffersize - nfilled, f->f_fp, (PyObject*)f);
FILE_END_ALLOW_THREADS(f)
shortread = (nread < buffersize - nfilled);
}
if (nread == 0) {
sizehint = 0;
if (!ferror(f->f_fp))
break;
if (errno == EINTR) {
if (PyErr_CheckSignals()) {
goto error;
}
clearerr(f->f_fp);
shortread = 0;
continue;
}
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
goto error;
}
totalread += nread;
p = (char*)memchr(buffer + nfilled, '\n', nread);
if (p == NULL) {
/* Need a larger buffer to fit this line */
nfilled += nread;
buffersize *= 2;
if (buffersize > PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError, "line is longer than a Python string can hold");
goto error;
}
if (big_buffer == NULL) {
/* Create the big buffer */
big_buffer = PyString_FromStringAndSize(NULL, buffersize);
if (big_buffer == NULL)
goto error;
buffer = PyString_AS_STRING(big_buffer);
memcpy(buffer, small_buffer, nfilled);
} else {
/* Grow the big buffer */
if (_PyString_Resize(&big_buffer, buffersize) < 0)
goto error;
buffer = PyString_AS_STRING(big_buffer);
}
continue;
}
end = buffer + nfilled + nread;
q = buffer;
do {
/* Process complete lines */
p++;
line = PyString_FromStringAndSize(q, p - q);
if (line == NULL)
goto error;
err = PyList_Append(list, line);
Py_DECREF(line);
if (err != 0)
goto error;
q = p;
p = (char*)memchr(q, '\n', end - q);
} while (p != NULL);
/* Move the remaining incomplete line to the start */
nfilled = end - q;
memmove(buffer, q, nfilled);
if (sizehint > 0)
if (totalread >= (size_t)sizehint)
break;
}
if (nfilled != 0) {
/* Partial last line */
line = PyString_FromStringAndSize(buffer, nfilled);
if (line == NULL)
goto error;
if (sizehint > 0) {
/* Need to complete the last line */
PyObject* rest = get_line(f, 0);
if (rest == NULL) {
Py_DECREF(line);
goto error;
}
PyString_Concat(&line, rest);
Py_DECREF(rest);
if (line == NULL)
goto error;
}
err = PyList_Append(list, line);
Py_DECREF(line);
if (err != 0)
goto error;
}
cleanup:
Py_XDECREF(big_buffer);
return list;
error:
Py_CLEAR(list);
goto cleanup;
}
Box* fileIterNext(BoxedFile* s) {
Box* rtn = fileReadline1(s);
assert(!rtn || rtn->cls == str_cls);
if (!rtn || ((BoxedString*)rtn)->s().empty())
raiseExcHelper(StopIteration, (const char*)NULL);
return rtn;
}
bool fileEof(BoxedFile* self) {
char ch = fgetc(self->f_fp);
ungetc(ch, self->f_fp);
return feof(self->f_fp);
}
Box* fileIterHasNext(Box* s) {
assert(s->cls == file_cls);
BoxedFile* self = static_cast<BoxedFile*>(s);
return boxBool(!fileEof(self));
}
extern "C" void PyFile_IncUseCount(PyFileObject* _f) noexcept {
BoxedFile* f = reinterpret_cast<BoxedFile*>(_f);
assert(f->cls == file_cls);
f->unlocked_count++;
}
extern "C" void PyFile_DecUseCount(PyFileObject* _f) noexcept {
BoxedFile* f = reinterpret_cast<BoxedFile*>(_f);
assert(f->cls == file_cls);
f->unlocked_count--;
assert(f->unlocked_count >= 0);
}
extern "C" void PyFile_SetFP(PyObject* _f, FILE* fp) noexcept {
assert(_f->cls == file_cls);
BoxedFile* f = static_cast<BoxedFile*>(_f);
assert(f->f_fp == NULL);
f->f_fp = fp;
}
extern "C" PyObject* PyFile_FromFile(FILE* fp, const char* name, const char* mode, int (*close)(FILE*)) noexcept {
return new BoxedFile(fp, name, mode, close);
}
extern "C" FILE* PyFile_AsFile(PyObject* f) noexcept {
if (!f || !PyFile_Check(f))
return NULL;
return static_cast<BoxedFile*>(f)->f_fp;
}
extern "C" int PyFile_WriteObject(PyObject* v, PyObject* f, int flags) noexcept {
PyObject* writer, *value, *args, *result;
if (f == NULL) {
PyErr_SetString(PyExc_TypeError, "writeobject with NULL file");
return -1;
} else if (PyFile_Check(f)) {
BoxedFile* fobj = (BoxedFile*)f;
#ifdef Py_USING_UNICODE
PyObject* enc = fobj->f_encoding;
int result;
#endif
if (fobj->f_fp == NULL) {
err_closed();
return -1;
}
#ifdef Py_USING_UNICODE
if ((flags & Py_PRINT_RAW) && PyUnicode_Check(v) && enc != Py_None) {
char* cenc = PyString_AS_STRING(enc);
const char* errors = fobj->f_errors == Py_None ? "strict" : PyString_AS_STRING(fobj->f_errors);
value = PyUnicode_AsEncodedString(v, cenc, errors);
if (value == NULL)
return -1;
} else {
value = v;
Py_INCREF(value);
}
// Pyston change:
// result = file_PyObject_Print(value, fobj, flags);
result = PyObject_Print(value, fobj->f_fp, flags);
Py_DECREF(value);
return result;
#else
// Pyston change:
// return file_PyObject_Print(v, fobj, flags);
return PyObject_Print(v, fobj->f_fp, flags);
#endif
}
writer = PyObject_GetAttrString(f, "write");
if (writer == NULL)
return -1;
if (flags & Py_PRINT_RAW) {
if (PyUnicode_Check(v)) {
value = v;
Py_INCREF(value);
} else
value = PyObject_Str(v);
} else
value = PyObject_Repr(v);
if (value == NULL) {
Py_DECREF(writer);
return -1;
}
args = PyTuple_Pack(1, value);
if (args == NULL) {
Py_DECREF(value);
Py_DECREF(writer);
return -1;
}
result = PyEval_CallObject(writer, args);
Py_DECREF(args);
Py_DECREF(value);
Py_DECREF(writer);
if (result == NULL)
return -1;
Py_DECREF(result);
return 0;
}
extern "C" int PyFile_WriteString(const char* s, PyObject* f) noexcept {
if (f == NULL) {
/* Should be caused by a pre-existing error */
if (!PyErr_Occurred())
PyErr_SetString(PyExc_SystemError, "null file for PyFile_WriteString");
return -1;
} else if (PyFile_Check(f)) {
BoxedFile* fobj = (BoxedFile*)f;
FILE* fp = PyFile_AsFile(f);
if (fp == NULL) {
err_closed();
return -1;
}
FILE_BEGIN_ALLOW_THREADS(fobj)
fputs(s, fp);
FILE_END_ALLOW_THREADS(fobj)
return 0;
} else if (!PyErr_Occurred()) {
PyObject* v = PyString_FromString(s);
int err;
if (v == NULL)
return -1;
err = PyFile_WriteObject(v, f, Py_PRINT_RAW);
Py_DECREF(v);
return err;
} else
return -1;
}
extern "C" void PyFile_SetBufSize(PyObject* f, int bufsize) noexcept {
assert(f->cls == file_cls);
BoxedFile* file = (BoxedFile*)f;
if (bufsize >= 0) {
int type;
switch (bufsize) {
case 0:
type = _IONBF;
break;
#ifdef HAVE_SETVBUF
case 1:
type = _IOLBF;
bufsize = BUFSIZ;
break;
#endif
default:
type = _IOFBF;
#ifndef HAVE_SETVBUF
bufsize = BUFSIZ;
#endif
break;
}
fflush(file->f_fp);
if (type == _IONBF) {
PyMem_Free(file->f_setbuf);
file->f_setbuf = NULL;
} else {
file->f_setbuf = (char*)PyMem_Realloc(file->f_setbuf, bufsize);
}
#ifdef HAVE_SETVBUF
setvbuf(file->f_fp, file->f_setbuf, type, bufsize);
#else /* !HAVE_SETVBUF */
setbuf(file->f_fp, file->f_setbuf);
#endif /* !HAVE_SETVBUF */
}
}
/* Set the encoding used to output Unicode strings.
Return 1 on success, 0 on failure. */
extern "C" int PyFile_SetEncoding(PyObject* f, const char* enc) noexcept {
return PyFile_SetEncodingAndErrors(f, enc, NULL);
}
extern "C" PyObject* PyFile_GetEncoding(PyObject* f) noexcept {
return static_cast<BoxedFile*>(f)->f_encoding;
}
extern "C" int PyFile_SetEncodingAndErrors(PyObject* f, const char* enc, char* errors) noexcept {
BoxedFile* file = static_cast<BoxedFile*>(f);
PyObject* str, *oerrors;
assert(PyFile_Check(f));
str = PyString_FromString(enc);
if (!str)
return 0;
if (errors) {
oerrors = PyString_FromString(errors);
if (!oerrors) {
Py_DECREF(str);
return 0;
}
} else {
oerrors = Py_None;
Py_INCREF(Py_None);
}
Py_DECREF(file->f_encoding);
file->f_encoding = str;
Py_DECREF(file->f_errors);
file->f_errors = oerrors;
return 1;
}
extern "C" int _PyFile_SanitizeMode(char* mode) noexcept {
char* upos;
size_t len = strlen(mode);
if (!len) {
PyErr_SetString(PyExc_ValueError, "empty mode string");
return -1;
}
upos = strchr(mode, 'U');
if (upos) {
memmove(upos, upos + 1, len - (upos - mode)); /* incl null char */
if (mode[0] == 'w' || mode[0] == 'a') {
PyErr_Format(PyExc_ValueError, "universal newline "
"mode can only be used with modes "
"starting with 'r'");
return -1;
}
if (mode[0] != 'r') {
memmove(mode + 1, mode, strlen(mode) + 1);
mode[0] = 'r';
}
if (!strchr(mode, 'b')) {
memmove(mode + 2, mode + 1, strlen(mode));
mode[1] = 'b';
}
} else if (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') {
PyErr_Format(PyExc_ValueError, "mode string must begin with "
"one of 'r', 'w', 'a' or 'U', not '%.200s'",
mode);
return -1;
}
#ifdef Py_VERIFY_WINNT
/* additional checks on NT with visual studio 2005 and higher */
if (!_PyVerify_Mode_WINNT(mode)) {
PyErr_Format(PyExc_ValueError, "Invalid mode ('%.50s')", mode);
return -1;
}
#endif
return 0;
}
extern "C" int PyObject_AsFileDescriptor(PyObject* o) noexcept {
int fd;
PyObject* meth;
if (PyInt_Check(o)) {
fd = _PyInt_AsInt(o);
} else if (PyLong_Check(o)) {
fd = _PyLong_AsInt(o);
} else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL) {
PyObject* fno = PyEval_CallObject(meth, NULL);
Py_DECREF(meth);
if (fno == NULL)
return -1;
if (PyInt_Check(fno)) {
fd = _PyInt_AsInt(fno);
Py_DECREF(fno);
} else if (PyLong_Check(fno)) {
fd = _PyLong_AsInt(fno);
Py_DECREF(fno);
} else {
PyErr_SetString(PyExc_TypeError, "fileno() returned a non-integer");
Py_DECREF(fno);
return -1;
}
} else {
PyErr_SetString(PyExc_TypeError, "argument must be an int, or have a fileno() method.");
return -1;
}
if (fd < 0) {
PyErr_Format(PyExc_ValueError, "file descriptor cannot be a negative integer (%i)", fd);
return -1;
}
return fd;
}
extern "C" int PyFile_SoftSpace(PyObject* f, int newflag) noexcept {
try {
return softspace(f, newflag);
} catch (ExcInfo e) {
return 0;
}
}
extern "C" PyObject* PyFile_GetLine(PyObject* f, int n) noexcept {
PyObject* result;
if (f == NULL) {
PyErr_BadInternalCall();
return NULL;
}
if (PyFile_Check(f)) {
BoxedFile* fo = (BoxedFile*)f;
if (fo->f_fp == NULL)
return err_closed();
if (!fo->readable)
return err_mode("reading");
/* refuse to mix with f.next() */
if (fo->f_buf != NULL && (fo->f_bufend - fo->f_bufptr) > 0 && fo->f_buf[0] != '\0')
return err_iterbuffered();
result = get_line(fo, n);
} else {
PyObject* reader;
PyObject* args;
reader = PyObject_GetAttrString(f, "readline");
if (reader == NULL)
return NULL;
if (n <= 0)
args = PyTuple_New(0);
else
args = Py_BuildValue("(i)", n);
if (args == NULL) {
Py_DECREF(reader);
return NULL;
}
result = PyEval_CallObject(reader, args);
Py_DECREF(reader);
Py_DECREF(args);
if (result != NULL && !PyString_Check(result) && !PyUnicode_Check(result)) {
Py_DECREF(result);
result = NULL;
PyErr_SetString(PyExc_TypeError, "object.readline() returned non-string");
}
}
if (n < 0 && result != NULL && PyString_Check(result)) {
char* s = PyString_AS_STRING(result);
Py_ssize_t len = PyString_GET_SIZE(result);
if (len == 0) {
Py_DECREF(result);
result = NULL;
PyErr_SetString(PyExc_EOFError, "EOF when reading a line");
} else if (s[len - 1] == '\n') {
if (/*result->ob_refcnt*/ 2 == 1) {
if (_PyString_Resize(&result, len - 1))
return NULL;
} else {
PyObject* v;
v = PyString_FromStringAndSize(s, len - 1);
Py_DECREF(result);
result = v;
}
}
}
#ifdef Py_USING_UNICODE
if (n < 0 && result != NULL && PyUnicode_Check(result)) {
Py_UNICODE* s = PyUnicode_AS_UNICODE(result);
Py_ssize_t len = PyUnicode_GET_SIZE(result);
if (len == 0) {
Py_DECREF(result);
result = NULL;
PyErr_SetString(PyExc_EOFError, "EOF when reading a line");
} else if (s[len - 1] == '\n') {
if (/*result->ob_refcnt*/ 2 == 1)
PyUnicode_Resize(&result, len - 1);
else {
PyObject* v;
v = PyUnicode_FromUnicode(s, len - 1);
Py_DECREF(result);
result = v;
}
}
}
#endif
return result;
}
/*
** Py_UniversalNewlineFread is an fread variation that understands
** all of \r, \n and \r\n conventions.
** The stream should be opened in binary mode.
** fobj must be a PyFileObject. In this case there
** is no readahead but in stead a flag is used to skip a following
** \n on the next read. Also, if the file is open in binary mode
** the whole conversion is skipped. Finally, the routine keeps track of
** the different types of newlines seen.
*/
extern "C" size_t Py_UniversalNewlineFread(char* buf, size_t n, FILE* stream, PyObject* fobj) noexcept {
char* dst = buf;
BoxedFile* f = (BoxedFile*)fobj;
int newlinetypes, skipnextlf;
assert(buf != NULL);
assert(stream != NULL);
if (!fobj || !PyFile_Check(fobj)) {
errno = ENXIO; /* What can you do... */
return 0;
}
if (!f->f_univ_newline)
return fread(buf, 1, n, stream);
newlinetypes = f->f_newlinetypes;
skipnextlf = f->f_skipnextlf;
/* Invariant: n is the number of bytes remaining to be filled
* in the buffer.
*/
while (n) {
size_t nread;
int shortread;
char* src = dst;
nread = fread(dst, 1, n, stream);
assert(nread <= n);
if (nread == 0)
break;
n -= nread; /* assuming 1 byte out for each in; will adjust */
shortread = n != 0; /* true iff EOF or error */
while (nread--) {
char c = *src++;
if (c == '\r') {
/* Save as LF and set flag to skip next LF. */
*dst++ = '\n';
skipnextlf = 1;
} else if (skipnextlf && c == '\n') {
/* Skip LF, and remember we saw CR LF. */
skipnextlf = 0;
newlinetypes |= NEWLINE_CRLF;
++n;
} else {
/* Normal char to be stored in buffer. Also
* update the newlinetypes flag if either this
* is an LF or the previous char was a CR.
*/
if (c == '\n')
newlinetypes |= NEWLINE_LF;
else if (skipnextlf)
newlinetypes |= NEWLINE_CR;
*dst++ = c;
skipnextlf = 0;
}
}
if (shortread) {
/* If this is EOF, update type flags. */
if (skipnextlf && feof(stream))
newlinetypes |= NEWLINE_CR;
break;
}
}
f->f_newlinetypes = newlinetypes;
f->f_skipnextlf = skipnextlf;
return dst - buf;
}
static PyObject* file_isatty(BoxedFile* f) noexcept {
long res;
if (f->f_fp == NULL)
return err_closed();
FILE_BEGIN_ALLOW_THREADS(f)
res = isatty((int)fileno(f->f_fp));
FILE_END_ALLOW_THREADS(f)
return PyBool_FromLong(res);
}
static PyObject* get_closed(BoxedFile* f, void* closure) noexcept {
return PyBool_FromLong((long)(f->f_fp == 0));
}
static PyObject* file_truncate(BoxedFile* f, PyObject* args) {
Py_off_t newsize;
PyObject* newsizeobj = NULL;
Py_off_t initialpos;
int ret;
if (f->f_fp == NULL)
return err_closed();
if (!f->writable)
return err_mode("writing");
if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj))
return NULL;
/* Get current file position. If the file happens to be open for
* update and the last operation was an input operation, C doesn't
* define what the later fflush() will do, but we promise truncate()
* won't change the current position (and fflush() *does* change it
* then at least on Windows). The easiest thing is to capture
* current pos now and seek back to it at the end.
*/
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
initialpos = _portable_ftell(f->f_fp);
FILE_END_ALLOW_THREADS(f)
if (initialpos == -1)
goto onioerror;
/* Set newsize to current postion if newsizeobj NULL, else to the
* specified value.
*/
if (newsizeobj != NULL) {
#if !defined(HAVE_LARGEFILE_SUPPORT)
newsize = PyInt_AsLong(newsizeobj);
#else
newsize = PyLong_Check(newsizeobj) ? PyLong_AsLongLong(newsizeobj) : PyInt_AsLong(newsizeobj);
#endif
if (PyErr_Occurred())
return NULL;
} else /* default to current position */
newsize = initialpos;
/* Flush the stream. We're mixing stream-level I/O with lower-level
* I/O, and a flush may be necessary to synch both platform views
* of the current file state.
*/
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
ret = fflush(f->f_fp);
FILE_END_ALLOW_THREADS(f)
if (ret != 0)
goto onioerror;
#ifdef MS_WINDOWS
/* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
so don't even try using it. */
{
HANDLE hFile;
/* Have to move current pos to desired endpoint on Windows. */
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
ret = _portable_fseek(f->f_fp, newsize, SEEK_SET) != 0;
FILE_END_ALLOW_THREADS(f)
if (ret)
goto onioerror;
/* Truncate. Note that this may grow the file! */
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp));
ret = hFile == (HANDLE)-1;
if (ret == 0) {
ret = SetEndOfFile(hFile) == 0;
if (ret)
errno = EACCES;
}
FILE_END_ALLOW_THREADS(f)
if (ret)
goto onioerror;
}
#else
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
ret = ftruncate(fileno(f->f_fp), newsize);
FILE_END_ALLOW_THREADS(f)
if (ret != 0)
goto onioerror;
#endif /* !MS_WINDOWS */
/* Restore original file position. */
FILE_BEGIN_ALLOW_THREADS(f)
errno = 0;
ret = _portable_fseek(f->f_fp, initialpos, SEEK_SET) != 0;
FILE_END_ALLOW_THREADS(f)
if (ret)
goto onioerror;
Py_INCREF(Py_None);
return Py_None;
onioerror:
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
return NULL;
}
PyDoc_STRVAR(seek_doc, "seek(offset[, whence]) -> None. Move to new file position.\n"
"\n"
"Argument offset is a byte count. Optional argument whence defaults to\n"
"0 (offset from start of file, offset should be >= 0); other values are 1\n"
"(move relative to current position, positive or negative), and 2 (move\n"
"relative to end of file, usually negative, although many platforms allow\n"
"seeking beyond the end of a file). If the file is opened in text mode,\n"
"only offsets returned by tell() are legal. Use of other offsets causes\n"
"undefined behavior."
"\n"
"Note that not all file objects are seekable.");
PyDoc_STRVAR(truncate_doc, "truncate([size]) -> None. Truncate the file to at most size bytes.\n"
"\n"
"Size defaults to the current file position, as returned by tell().");
PyDoc_STRVAR(readlines_doc, "readlines([size]) -> list of strings, each a line from the file.\n"
"\n"
"Call readline() repeatedly and return a list of the lines so read.\n"
"The optional size argument, if given, is an approximate bound on the\n"
"total number of bytes in the lines returned.");
PyDoc_STRVAR(isatty_doc, "isatty() -> true or false. True if the file is connected to a tty device.");
static PyMethodDef file_methods[] = {
{ "seek", (PyCFunction)file_seek, METH_VARARGS, seek_doc },
{ "truncate", (PyCFunction)file_truncate, METH_VARARGS, truncate_doc },
{ "readlines", (PyCFunction)file_readlines, METH_VARARGS, readlines_doc },
{ "writelines", (PyCFunction)file_writelines, METH_O, NULL },
{ "isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc },
};
static PyGetSetDef file_getsetlist[] = {
{ "closed", (getter)get_closed, NULL, "True if the file is closed", NULL },
};
void fileDestructor(Box* b) {
assert(isSubclass(b->cls, file_cls));
BoxedFile* self = static_cast<BoxedFile*>(b);
if (self->f_fp && self->f_close)
self->f_close(self->f_fp);
self->f_fp = NULL;
}
void BoxedFile::gcHandler(GCVisitor* v, Box* b) {
Box::gcHandler(v, b);
assert(isSubclass(b->cls, file_cls));
BoxedFile* f = static_cast<BoxedFile*>(b);
v->visit(&f->f_name);
v->visit(&f->f_mode);
v->visit(&f->f_encoding);
v->visit(&f->f_errors);
v->visit(&f->f_setbuf);
}
void setupFile() {
file_cls->tp_dealloc = fileDestructor;
file_cls->has_safe_tp_dealloc = true;
file_cls->giveAttr(
"read", new BoxedFunction(FunctionMetadata::create((void*)fileRead, STR, 2, false, false), { boxInt(-1) }));
FunctionMetadata* readline = FunctionMetadata::create((void*)fileReadline1, STR, 1);
file_cls->giveAttr("readline", new BoxedFunction(readline));
file_cls->giveAttr("flush", new BoxedFunction(FunctionMetadata::create((void*)fileFlush, NONE, 1)));
file_cls->giveAttr("write", new BoxedFunction(FunctionMetadata::create((void*)fileWrite, NONE, 2)));
file_cls->giveAttr("close", new BoxedFunction(FunctionMetadata::create((void*)fileClose, UNKNOWN, 1)));
file_cls->giveAttr("fileno", new BoxedFunction(FunctionMetadata::create((void*)fileFileno, BOXED_INT, 1)));
file_cls->giveAttr("__repr__", new BoxedFunction(FunctionMetadata::create((void*)fileRepr, STR, 1)));
file_cls->giveAttr("__enter__",
new BoxedFunction(FunctionMetadata::create((void*)fileEnter, typeFromClass(file_cls), 1)));
file_cls->giveAttr("__exit__", new BoxedFunction(FunctionMetadata::create((void*)fileExit, UNKNOWN, 4)));
file_cls->giveAttr("__iter__", file_cls->getattr(internStringMortal("__enter__")));
file_cls->giveAttr("__hasnext__",
new BoxedFunction(FunctionMetadata::create((void*)fileIterHasNext, BOXED_BOOL, 1)));
file_cls->giveAttr("next", new BoxedFunction(FunctionMetadata::create((void*)fileIterNext, STR, 1)));
file_cls->giveAttr("tell", new BoxedFunction(FunctionMetadata::create((void*)fileTell, UNKNOWN, 1)));
file_cls->giveAttr("softspace",
new BoxedMemberDescriptor(BoxedMemberDescriptor::INT, offsetof(BoxedFile, f_softspace), false));
file_cls->giveAttr("name",
new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, offsetof(BoxedFile, f_name), true));
file_cls->giveAttr("mode",
new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, offsetof(BoxedFile, f_mode), true));
file_cls->giveAttr("encoding",
new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, offsetof(BoxedFile, f_encoding), true));
file_cls->giveAttr("__new__", new BoxedFunction(FunctionMetadata::create((void*)fileNew, UNKNOWN, 4, false, false),
{ boxString("r"), boxInt(-1) }));
for (auto& md : file_methods) {
file_cls->giveAttr(md.ml_name, new BoxedMethodDescriptor(&md, file_cls));
}
for (auto& getset : file_getsetlist) {
file_cls->giveAttr(getset.name, new (capi_getset_cls) BoxedGetsetDescriptor(
internStringMortal(getset.name), getset.get,
(void (*)(Box*, Box*, void*))getset.set, getset.closure));
}
file_cls->freeze();
}
void teardownFile() {
}
}
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PYSTON_RUNTIME_FILE_H
#define PYSTON_RUNTIME_FILE_H
#include "core/types.h"
#include "runtime/types.h"
namespace pyston {
class BoxedFile : public Box {
public:
PyObject_HEAD;
FILE* f_fp;
PyObject* f_name;
PyObject* f_mode;
int (*f_close)(FILE*);
int f_softspace; /* Flag used by 'print' command */
int f_binary; /* Flag which indicates whether the file is
open in binary (1) or text (0) mode */
char* f_buf; /* Allocated readahead buffer */
char* f_bufend; /* Points after last occupied position */
char* f_bufptr; /* Current buffer position */
char* f_setbuf; /* Buffer for setbuf(3) and setvbuf(3) */
int f_univ_newline; /* Handle any newline convention */
int f_newlinetypes; /* Types of newlines seen */
int f_skipnextlf; /* Skip next \n */
PyObject* f_encoding;
PyObject* f_errors;
PyObject* weakreflist; /* List of weak references */
int unlocked_count; /* Num. currently running sections of code
using f_fp with the GIL released. */
int readable;
int writable;
BoxedFile(FILE* f, std::string fname, const char* fmode, int (*close)(FILE*) = fclose)
__attribute__((visibility("default")));
DEFAULT_CLASS(file_cls);
static void gcHandler(GCVisitor* v, Box* b);
};
}
#endif
......@@ -735,7 +735,7 @@ Box* impFindModule(Box* _name, BoxedList* path) {
if (sr.type == SearchResult::PY_SOURCE) {
Box* path = boxString(sr.path);
Box* mode = boxString("r");
Box* f = runtimeCall(file_cls, ArgPassSpec(2), path, mode, NULL, NULL, NULL);
Box* f = runtimeCall(&PyFile_Type, ArgPassSpec(2), path, mode, NULL, NULL, NULL);
return BoxedTuple::create({ f, path, BoxedTuple::create({ boxString(".py"), mode, boxInt(sr.type) }) });
}
......@@ -748,7 +748,7 @@ Box* impFindModule(Box* _name, BoxedList* path) {
if (sr.type == SearchResult::C_EXTENSION) {
Box* path = boxString(sr.path);
Box* mode = boxString("rb");
Box* f = runtimeCall(file_cls, ArgPassSpec(2), path, mode, NULL, NULL, NULL);
Box* f = runtimeCall(&PyFile_Type, ArgPassSpec(2), path, mode, NULL, NULL, NULL);
return BoxedTuple::create({ f, path, BoxedTuple::create({ boxString(".so"), mode, boxInt(sr.type) }) });
}
......@@ -783,7 +783,7 @@ Box* impLoadModule(Box* _name, Box* _file, Box* _pathname, Box** args) {
RELEASE_ASSERT(_file == None, "");
return createAndRunModule(name, (llvm::Twine(pathname->s()) + "/__init__.py").str(), pathname->s());
} else if (type->n == SearchResult::PY_SOURCE) {
RELEASE_ASSERT(_file->cls == file_cls, "");
RELEASE_ASSERT(_file->cls == &PyFile_Type, "");
return createAndRunModule(name, pathname->s());
}
......
......@@ -48,7 +48,6 @@ AST_stmt* _asttmt_forcer;
#define FORCE(name) forceLink((void*)name)
void force() {
FORCE(softspace);
FORCE(my_assert);
FORCE(boxInt);
......
......@@ -40,7 +40,6 @@
#include "gc/gc.h"
#include "runtime/classobj.h"
#include "runtime/dict.h"
#include "runtime/file.h"
#include "runtime/float.h"
#include "runtime/generator.h"
#include "runtime/hiddenclass.h"
......@@ -141,74 +140,65 @@ extern "C" Box* deopt(AST_expr* expr, Box* value) {
return astInterpretDeopt(deopt_state.cf->md, expr, deopt_state.current_stmt, value, deopt_state.frame_state);
}
extern "C" bool softspace(Box* b, bool newval) {
assert(b);
// TODO do we also need to wrap the isSubclass in the try{}? it
// can throw exceptions which would bubble up from print
// statements.
if (isSubclass(b->cls, file_cls)) {
int& ss = static_cast<BoxedFile*>(b)->f_softspace;
int r = ss;
ss = newval;
assert(r == 0 || r == 1);
return (bool)r;
}
static BoxedString* softspace_str = internStringImmortal("softspace");
bool r;
Box* gotten = NULL;
try {
Box* gotten = getattrInternal<CXX>(b, softspace_str);
if (!gotten) {
r = 0;
} else {
r = nonzero(gotten);
extern "C" void printHelper(Box* w, Box* v, bool nl) {
if (w == None)
w = getSysStdout();
int err = 0;
// copied from cpythons PRINT_ITEM and PRINT_NEWLINE op handling code
if (v) {
/* PyFile_SoftSpace() can exececute arbitrary code
if sys.stdout is an instance with a __getattr__.
If __getattr__ raises an exception, w will
be freed, so we need to prevent that temporarily. */
Py_XINCREF(w);
if (w != NULL && PyFile_SoftSpace(w, 0))
err = PyFile_WriteString(" ", w);
if (err == 0)
err = PyFile_WriteObject(v, w, Py_PRINT_RAW);
if (err == 0) {
/* XXX move into writeobject() ? */
if (PyString_Check(v)) {
char* s = PyString_AS_STRING(v);
Py_ssize_t len = PyString_GET_SIZE(v);
if (len == 0 || !isspace(Py_CHARMASK(s[len - 1])) || s[len - 1] == ' ')
PyFile_SoftSpace(w, 1);
}
#ifdef Py_USING_UNICODE
else if (PyUnicode_Check(v)) {
Py_UNICODE* s = PyUnicode_AS_UNICODE(v);
Py_ssize_t len = PyUnicode_GET_SIZE(v);
if (len == 0 || !Py_UNICODE_ISSPACE(s[len - 1]) || s[len - 1] == ' ')
PyFile_SoftSpace(w, 1);
}
#endif
else
PyFile_SoftSpace(w, 1);
}
} catch (ExcInfo e) {
r = 0;
}
try {
setattr(b, softspace_str, boxInt(newval));
} catch (ExcInfo e) {
r = 0;
}
return r;
}
extern "C" void printHelper(Box* dest, Box* var, bool nl) {
static BoxedString* write_str = internStringImmortal("write");
static BoxedString* newline_str = internStringImmortal("\n");
static BoxedString* space_str = internStringImmortal(" ");
if (dest == None)
dest = getSysStdout();
if (var) {
// begin code for handling of softspace
bool new_softspace = !nl;
if (softspace(dest, new_softspace))
callattrInternal<CXX, NOT_REWRITABLE>(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1), space_str, 0, 0, 0,
0);
Box* str_or_unicode_var = (var->cls == unicode_cls) ? var : str(var);
Box* write_rtn = callattrInternal<CXX, NOT_REWRITABLE>(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1),
str_or_unicode_var, 0, 0, 0, 0);
if (!write_rtn)
raiseAttributeError(dest, write_str->s());
Py_XDECREF(w);
Py_DECREF(v);
// Py_XDECREF(stream);
// stream = NULL;
if (err != 0)
throwCAPIException();
}
if (nl) {
Box* write_rtn = callattrInternal<CXX, NOT_REWRITABLE>(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1),
newline_str, 0, 0, 0, 0);
if (!write_rtn)
raiseAttributeError(dest, write_str->s());
if (!var)
softspace(dest, false);
if (w != NULL) {
/* w.write() may replace sys.stdout, so we
* have to keep our reference to it */
Py_INCREF(w);
err = PyFile_WriteString("\n", w);
if (err == 0)
PyFile_SoftSpace(w, 0);
Py_DECREF(w);
}
// Py_XDECREF(stream);
}
if (err != 0)
throwCAPIException();
}
extern "C" void my_assert(bool b) {
......@@ -2645,7 +2635,7 @@ extern "C" bool nonzero(Box* obj) {
crewrite_args.assertReturnConvention(ReturnConvention::NO_RETURN);
}
ASSERT(obj->cls->is_user_defined || obj->cls->instances_are_nonzero || obj->cls == classobj_cls
|| obj->cls == type_cls || isSubclass(obj->cls, Exception) || obj->cls == file_cls
|| obj->cls == type_cls || isSubclass(obj->cls, Exception) || obj->cls == &PyFile_Type
|| obj->cls == traceback_cls || obj->cls == instancemethod_cls || obj->cls == module_cls
|| obj->cls == capifunc_cls || obj->cls == builtin_function_or_method_cls
|| obj->cls == method_cls || obj->cls == frame_cls || obj->cls == generator_cls
......
......@@ -48,7 +48,6 @@ __attribute__((format(printf, 2, 3)));
void raiseExcHelper(BoxedClass*, Box* arg) __attribute__((__noreturn__));
// TODO sort this
extern "C" bool softspace(Box* b, bool newval);
extern "C" void printHelper(Box* dest, Box* var, bool nl);
extern "C" void my_assert(bool b);
extern "C" Box* getattr(Box* obj, BoxedString* attr);
......
......@@ -37,7 +37,6 @@
#include "runtime/code.h"
#include "runtime/complex.h"
#include "runtime/dict.h"
#include "runtime/file.h"
#include "runtime/hiddenclass.h"
#include "runtime/ics.h"
#include "runtime/iterobject.h"
......@@ -1568,9 +1567,9 @@ void BoxedClosure::gcHandler(GCVisitor* v, Box* b) {
extern "C" {
BoxedClass* object_cls, *type_cls, *none_cls, *bool_cls, *int_cls, *float_cls,
* str_cls = NULL, *function_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls,
*file_cls, *member_descriptor_cls, *closure_cls, *generator_cls, *null_importer_cls, *complex_cls,
*basestring_cls, *property_cls, *staticmethod_cls, *classmethod_cls, *attrwrapper_cls, *pyston_getset_cls,
*capi_getset_cls, *builtin_function_or_method_cls, *attrwrapperiter_cls, *set_cls, *frozenset_cls;
*member_descriptor_cls, *closure_cls, *generator_cls, *null_importer_cls, *complex_cls, *basestring_cls,
*property_cls, *staticmethod_cls, *classmethod_cls, *attrwrapper_cls, *pyston_getset_cls, *capi_getset_cls,
*builtin_function_or_method_cls, *attrwrapperiter_cls, *set_cls, *frozenset_cls;
BoxedTuple* EmptyTuple;
}
......@@ -3629,6 +3628,11 @@ static Box* getsetDelete(Box* self, Box* obj) {
return getsetSet(self, obj, NULL);
}
static int _check_and_flush(FILE* stream) {
int prev_fail = ferror(stream);
return fflush(stream) || prev_fail ? EOF : 0;
}
bool TRACK_ALLOCATIONS = false;
void setupRuntime() {
......@@ -3704,8 +3708,6 @@ void setupRuntime() {
BoxedClass(object_cls, &AttrWrapper::gcHandler, 0, 0, sizeof(AttrWrapper), false, "attrwrapper");
dict_cls = new (0) BoxedClass(object_cls, &BoxedDict::gcHandler, 0, 0, sizeof(BoxedDict), false, "dict");
dict_cls->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS;
file_cls = new (0) BoxedClass(object_cls, &BoxedFile::gcHandler, 0, offsetof(BoxedFile, weakreflist),
sizeof(BoxedFile), false, "file");
int_cls = new (0) BoxedClass(object_cls, NULL, 0, 0, sizeof(BoxedInt), false, "int");
int_cls->tp_flags |= Py_TPFLAGS_INT_SUBCLASS;
bool_cls = new (0) BoxedClass(int_cls, NULL, 0, 0, sizeof(BoxedBool), false, "bool", false);
......@@ -3755,7 +3757,6 @@ void setupRuntime() {
pyston_getset_cls->tp_mro = BoxedTuple::create({ pyston_getset_cls, object_cls });
attrwrapper_cls->tp_mro = BoxedTuple::create({ attrwrapper_cls, object_cls });
dict_cls->tp_mro = BoxedTuple::create({ dict_cls, object_cls });
file_cls->tp_mro = BoxedTuple::create({ file_cls, object_cls });
int_cls->tp_mro = BoxedTuple::create({ int_cls, object_cls });
bool_cls->tp_mro = BoxedTuple::create({ bool_cls, object_cls });
complex_cls->tp_mro = BoxedTuple::create({ complex_cls, object_cls });
......@@ -3810,7 +3811,6 @@ void setupRuntime() {
pyston_getset_cls->finishInitialization();
attrwrapper_cls->finishInitialization();
dict_cls->finishInitialization();
file_cls->finishInitialization();
int_cls->finishInitialization();
bool_cls->finishInitialization();
complex_cls->finishInitialization();
......@@ -3954,7 +3954,6 @@ void setupRuntime() {
setupDict();
setupSet();
setupTuple();
setupFile();
setupGenerator();
setupIter();
setupClassobj();
......@@ -4101,6 +4100,21 @@ void setupRuntime() {
attrwrapperiter_cls->tp_iter = PyObject_SelfIter;
attrwrapperiter_cls->tp_iternext = AttrWrapperIter::next_capi;
PyType_Ready(&PyFile_Type);
PyObject* sysin, *sysout, *syserr;
sysin = PyFile_FromFile(stdin, "<stdin>", "r", NULL);
sysout = PyFile_FromFile(stdout, "<stdout>", "w", _check_and_flush);
syserr = PyFile_FromFile(stderr, "<stderr>", "w", _check_and_flush);
RELEASE_ASSERT(!PyErr_Occurred(), "");
sys_module->giveAttr("stdout", sysout);
sys_module->giveAttr("stdin", sysin);
sys_module->giveAttr("stderr", syserr);
sys_module->giveAttr("__stdout__", sys_module->getattr(internStringMortal("stdout")));
sys_module->giveAttr("__stdin__", sys_module->getattr(internStringMortal("stdin")));
sys_module->giveAttr("__stderr__", sys_module->getattr(internStringMortal("stderr")));
setupBuiltins();
_PyExc_Init();
setupThread();
......@@ -4117,6 +4131,7 @@ void setupRuntime() {
PyType_Ready(&PyCObject_Type);
PyType_Ready(&PyDictProxy_Type);
initerrno();
init_sha();
init_sha256();
......@@ -4213,7 +4228,6 @@ void teardownRuntime() {
teardownDict();
teardownSet();
teardownTuple();
teardownFile();
teardownDescr();
/*
......@@ -4233,7 +4247,6 @@ void teardownRuntime() {
clearAttrs(module_cls);
clearAttrs(dict_cls);
clearAttrs(tuple_cls);
clearAttrs(file_cls);
decref(bool_cls);
decref(int_cls);
......@@ -4246,7 +4259,6 @@ void teardownRuntime() {
decref(module_cls);
decref(dict_cls);
decref(tuple_cls);
decref(file_cls);
ASSERT(None->nrefs == 1, "%ld", None->nrefs);
decref(None);
......
......@@ -89,11 +89,11 @@ extern "C" BoxedString* EmptyString;
extern "C" {
extern BoxedClass* object_cls, *type_cls, *bool_cls, *int_cls, *long_cls, *float_cls, *str_cls, *function_cls,
*none_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls,
*enumerate_cls, *xrange_cls, *member_descriptor_cls, *null_importer_cls, *method_cls, *closure_cls, *generator_cls,
*complex_cls, *basestring_cls, *property_cls, *staticmethod_cls, *classmethod_cls, *attrwrapper_cls,
*pyston_getset_cls, *capi_getset_cls, *builtin_function_or_method_cls, *set_cls, *frozenset_cls, *code_cls,
*frame_cls, *capifunc_cls, *wrapperdescr_cls, *wrapperobject_cls;
*none_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *enumerate_cls,
*xrange_cls, *member_descriptor_cls, *null_importer_cls, *method_cls, *closure_cls, *generator_cls, *complex_cls,
*basestring_cls, *property_cls, *staticmethod_cls, *classmethod_cls, *attrwrapper_cls, *pyston_getset_cls,
*capi_getset_cls, *builtin_function_or_method_cls, *set_cls, *frozenset_cls, *code_cls, *frame_cls, *capifunc_cls,
*wrapperdescr_cls, *wrapperobject_cls;
}
#define unicode_cls (&PyUnicode_Type)
#define memoryview_cls (&PyMemoryView_Type)
......
......@@ -95,6 +95,7 @@ test_exceptions we are missing recursion-depth checking
test_extcall f(**kw) crashes if kw isn't a dict
test_file2k we abort when you try to open() a directory
test_file_eintr not sure
test_fileio [unknown]
test_fork1 [unknown]
test_frozen [unknown]
test_ftplib [unknown]
......@@ -186,7 +187,6 @@ test_site [unknown]
test_smtpnet [unknown]
test_socketserver [unknown]
test_socket [unknown]
test_softspace [unknown]
test_sort argument specification issue in listSort?
test_sqlite [unknown]
test_ssl [unknown]
......
......@@ -17,5 +17,5 @@ import subprocess
subprocess.check_call(["sed", "-i", 's/\\(def test_digest.*\\)/\\1\\n return/',
os.path.join(PYOPENSSL_DIR, "test", "test_crypto.py")])
expected = [{'ran': 247, 'errors': 2}]
expected = [{'ran': 247, 'errors': 1}]
run_test([NOSETESTS_EXE], cwd=PYOPENSSL_DIR, expected=expected)
# expected: fail
# - I don't feel like implementing this right now
class C(object):
def write(self, s):
print "class write", repr(s)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment