Skip to content

Commit 279c38d

Browse files
committed
[GR-23304] Add BufferedWriter/Random
PullRequest: graalpython/1612
2 parents 72fe589 + 0317528 commit 279c38d

29 files changed

+1982
-952
lines changed

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_memoryview.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,9 @@ def test_memoryview_acquire_release(self):
409409
assert mv3[2] == 126
410410
del mv1
411411
del mv3
412-
for i in range(10):
412+
for i in range(120):
413413
gc.collect()
414-
time.sleep(0.1)
414+
if obj.get_bufcount() == 0:
415+
break
416+
time.sleep(1)
415417
assert obj.get_bufcount() == 0
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
import unittest
41+
import _io
42+
43+
44+
class IOBaseTests(unittest.TestCase):
45+
46+
def test_iobase_ctor_accepts_anything(self):
47+
_io._IOBase()
48+
_io._IOBase(1, '2', kw=3)
49+
50+
def test_close(self):
51+
x = _io._IOBase()
52+
self.assertFalse(x.closed)
53+
self.assertFalse('__IOBase_closed' in dir(x))
54+
x.close()
55+
self.assertTrue(x.closed)
56+
self.assertEqual(True, getattr(x, '__IOBase_closed'))
57+
58+
def test_flush(self):
59+
x = _io._IOBase()
60+
x.flush()
61+
x.close()
62+
self.assertRaises(ValueError, x.flush)
63+
# the value of __IOBase_closed does not matter, only its presence
64+
setattr(x, '__IOBase_closed', False)
65+
self.assertRaises(ValueError, x.flush)
66+
delattr(x, '__IOBase_closed')
67+
x.flush()
68+
69+
def test_close_calls_flush_once(self):
70+
flush_called = 0
71+
72+
class X(_io._IOBase):
73+
def flush(self):
74+
nonlocal flush_called
75+
flush_called += 1
76+
77+
x = X()
78+
x.close()
79+
self.assertEqual(1, flush_called)
80+
x.close()
81+
self.assertEqual(1, flush_called)
82+
# the value of __IOBase_closed does not matter, only its presence
83+
setattr(x, '__IOBase_closed', False)
84+
x.close()
85+
self.assertEqual(1, flush_called)
86+
delattr(x, '__IOBase_closed')
87+
x.close()
88+
self.assertEqual(2, flush_called)
89+
90+
def test_close_chains_exceptions(self):
91+
class X(_io._IOBase):
92+
def flush(self):
93+
raise ValueError('abc')
94+
95+
def __setattr__(self, key, value):
96+
raise ValueError('xyz')
97+
98+
try:
99+
X().close()
100+
self.fail('close() did not raise an exception')
101+
except ValueError as e:
102+
self.assertEqual('xyz', e.args[0])
103+
self.assertEqual('abc', e.__context__.args[0])
104+
105+
def test_unsupported(self):
106+
self.assertRaises(_io.UnsupportedOperation, _io._IOBase().seek)
107+
self.assertRaises(_io.UnsupportedOperation, _io._IOBase().truncate)
108+
self.assertRaises(_io.UnsupportedOperation, _io._IOBase().fileno)
109+
110+
def test_tell_call_seek(self):
111+
check = self.assertEqual
112+
113+
class X(_io._IOBase):
114+
def seek(self, *args):
115+
check((0, 1), args)
116+
return 42
117+
118+
self.assertEqual(42, X().tell())
119+
120+
def test_check_closed(self):
121+
ret_val = False
122+
123+
class X(_io._IOBase):
124+
@property
125+
def closed(self):
126+
return ret_val
127+
128+
X()._checkClosed()
129+
ret_val = True
130+
self.assertRaises(ValueError, X()._checkClosed)
131+
ret_val = 42 # _checkClosed accepts anything that evaluates as True
132+
self.assertRaises(ValueError, X()._checkClosed)
133+
ret_val = (1, )
134+
self.assertRaises(ValueError, X()._checkClosed)
135+
136+
def test_check_seekable(self):
137+
self.assertFalse(_io._IOBase().seekable())
138+
ret_val = False
139+
140+
class X(_io._IOBase):
141+
def seekable(self):
142+
return ret_val
143+
144+
self.assertRaises(_io.UnsupportedOperation, X()._checkSeekable)
145+
ret_val = True
146+
self.assertTrue(X()._checkSeekable())
147+
ret_val = 42 # _checkSeekable accepts only explicit True
148+
self.assertRaises(_io.UnsupportedOperation, X()._checkSeekable)
149+
ret_val = (1, )
150+
self.assertRaises(_io.UnsupportedOperation, X()._checkSeekable)
151+
152+
def test_check_readable(self):
153+
self.assertFalse(_io._IOBase().readable())
154+
ret_val = False
155+
156+
class X(_io._IOBase):
157+
def readable(self):
158+
return ret_val
159+
160+
self.assertRaises(_io.UnsupportedOperation, X()._checkReadable)
161+
ret_val = True
162+
self.assertTrue(X()._checkReadable())
163+
ret_val = 42 # _checkReadable accepts only explicit True
164+
self.assertRaises(_io.UnsupportedOperation, X()._checkReadable)
165+
ret_val = (1, )
166+
self.assertRaises(_io.UnsupportedOperation, X()._checkReadable)
167+
168+
def test_check_writable(self):
169+
self.assertFalse(_io._IOBase().writable())
170+
ret_val = False
171+
172+
class X(_io._IOBase):
173+
def writable(self):
174+
return ret_val
175+
176+
self.assertRaises(_io.UnsupportedOperation, X()._checkWritable)
177+
ret_val = True
178+
self.assertTrue(X()._checkWritable())
179+
ret_val = 42 # _checkWritable accepts only explicit True
180+
self.assertRaises(_io.UnsupportedOperation, X()._checkWritable)
181+
ret_val = (1, )
182+
self.assertRaises(_io.UnsupportedOperation, X()._checkWritable)
183+
184+
def test_check_properties(self):
185+
class X(_io._IOBase):
186+
@property
187+
def seekable(self):
188+
return True
189+
@property
190+
def readable(self):
191+
return True
192+
@property
193+
def writable(self):
194+
return True
195+
# _checkSeekable calls seekable(), but we define it as a property by mistake
196+
self.assertRaises(TypeError, X()._checkSeekable)
197+
self.assertRaises(TypeError, X()._checkReadable)
198+
self.assertRaises(TypeError, X()._checkWritable)
199+
200+
def test_enter(self):
201+
x = _io._IOBase()
202+
self.assertIs(x, x.__enter__())
203+
x.close()
204+
self.assertRaises(ValueError, x.__enter__)
205+
206+
def test_exit_dispatches_to_close(self):
207+
class X(_io._IOBase):
208+
def close(self):
209+
return 42
210+
211+
self.assertEqual(42, X().__exit__())
212+
213+
def test_exit_accepts_varargs(self):
214+
x = _io._IOBase()
215+
x.__exit__(1, 2, 3)
216+
with self.assertRaises(TypeError):
217+
x.__exit__(kw=1)
218+
219+
def test_isatty(self):
220+
x = _io._IOBase()
221+
self.assertFalse(x.isatty())
222+
x.close()
223+
self.assertRaises(ValueError, x.isatty)
224+
225+
def test_iter(self):
226+
x = _io._IOBase()
227+
self.assertIs(x, x.__iter__())
228+
x.close()
229+
self.assertRaises(ValueError, x.__iter__)
230+
231+
def test_methods_do_not_dispatch_to_checkClosed(self):
232+
class X(_io._IOBase):
233+
def _checkClosed(self):
234+
raise NotImplementedError()
235+
236+
x = X()
237+
self.assertIs(x, x.__enter__())
238+
self.assertIs(x, x.__iter__())
239+
self.assertFalse(x.isatty())
240+
x.writelines([])
241+
242+
def test_next(self):
243+
it = iter(['aaa', 'bbb', ''])
244+
245+
class X(_io._IOBase):
246+
def readline(self, limit=-1):
247+
return next(it)
248+
x = iter(X())
249+
self.assertEqual('aaa', next(x))
250+
self.assertEqual('bbb', next(x))
251+
self.assertRaises(StopIteration, next, x)
252+
253+
def test_writelines(self):
254+
buf = []
255+
256+
class X(_io._IOBase):
257+
def write(self, x):
258+
buf.append(x)
259+
X().writelines(['aaa', 'bbb'])
260+
self.assertEqual(['aaa', 'bbb'], buf)
261+
262+
def test_writelines_err(self):
263+
self.assertRaises(AttributeError, _io._IOBase().writelines, ['aaa', 'bbb'])
264+
265+
266+
if __name__ == '__main__':
267+
unittest.main()

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@
7474
import com.oracle.truffle.api.CallTarget;
7575
import com.oracle.truffle.api.CompilerAsserts;
7676
import com.oracle.truffle.api.CompilerDirectives;
77+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
78+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
7779
import com.oracle.truffle.api.RootCallTarget;
7880
import com.oracle.truffle.api.Truffle;
7981
import com.oracle.truffle.api.TruffleFile;
8082
import com.oracle.truffle.api.TruffleLanguage;
8183
import com.oracle.truffle.api.TruffleLogger;
82-
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
83-
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
8484
import com.oracle.truffle.api.debug.DebuggerTags;
8585
import com.oracle.truffle.api.frame.Frame;
8686
import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -95,9 +95,9 @@
9595
import com.oracle.truffle.api.library.ExportMessage;
9696
import com.oracle.truffle.api.nodes.ExecutableNode;
9797
import com.oracle.truffle.api.nodes.ExplodeLoop;
98+
import com.oracle.truffle.api.nodes.ExplodeLoop.LoopExplosionKind;
9899
import com.oracle.truffle.api.nodes.Node;
99100
import com.oracle.truffle.api.nodes.RootNode;
100-
import com.oracle.truffle.api.nodes.ExplodeLoop.LoopExplosionKind;
101101
import com.oracle.truffle.api.object.Shape;
102102
import com.oracle.truffle.api.source.Source;
103103
import com.oracle.truffle.api.source.Source.SourceBuilder;
@@ -705,7 +705,11 @@ public Shape getBuiltinTypeInstanceShape(PythonBuiltinClassType type) {
705705
Shape shape = builtinTypeInstanceShapes[ordinal];
706706
if (shape == null) {
707707
CompilerDirectives.transferToInterpreterAndInvalidate();
708-
shape = Shape.newBuilder(getEmptyShape()).addConstantProperty(HiddenAttributes.CLASS, type, 0).build();
708+
Shape.DerivedBuilder shapeBuilder = Shape.newBuilder(getEmptyShape()).addConstantProperty(HiddenAttributes.CLASS, type, 0);
709+
if (!type.isBuiltinWithDict()) {
710+
shapeBuilder.shapeFlags(PythonObject.HAS_SLOTS_BUT_NO_DICT_FLAG);
711+
}
712+
shape = shapeBuilder.build();
709713
builtinTypeInstanceShapes[ordinal] = shape;
710714
}
711715
return shape;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,12 @@
106106
import com.oracle.graal.python.builtins.modules.bz2.BZ2DecompressorBuiltins;
107107
import com.oracle.graal.python.builtins.modules.bz2.BZ2ModuleBuiltins;
108108
import com.oracle.graal.python.builtins.modules.io.BufferedIOBaseBuiltins;
109+
import com.oracle.graal.python.builtins.modules.io.BufferedIOMixinBuiltins;
110+
import com.oracle.graal.python.builtins.modules.io.BufferedRandomBuiltins;
109111
import com.oracle.graal.python.builtins.modules.io.BufferedReaderBuiltins;
112+
import com.oracle.graal.python.builtins.modules.io.BufferedReaderMixinBuiltins;
113+
import com.oracle.graal.python.builtins.modules.io.BufferedWriterBuiltins;
114+
import com.oracle.graal.python.builtins.modules.io.BufferedWriterMixinBuiltins;
110115
import com.oracle.graal.python.builtins.modules.io.FileIOBuiltins;
111116
import com.oracle.graal.python.builtins.modules.io.IOBaseBuiltins;
112117
import com.oracle.graal.python.builtins.modules.io.IOModuleBuiltins;
@@ -392,6 +397,11 @@ private static PythonBuiltins[] initializeBuiltins(boolean nativeAccessAllowed)
392397
new RawIOBaseBuiltins(),
393398
new TextIOBaseBuiltins(),
394399
new BufferedReaderBuiltins(),
400+
new BufferedWriterBuiltins(),
401+
new BufferedRandomBuiltins(),
402+
new BufferedReaderMixinBuiltins(),
403+
new BufferedWriterMixinBuiltins(),
404+
new BufferedIOMixinBuiltins(),
395405
new FileIOBuiltins(),
396406

397407
new StringModuleBuiltins(),

0 commit comments

Comments
 (0)