Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gevent
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
gevent
Commits
a208118f
Commit
a208118f
authored
Mar 25, 2020
by
Jason Madden
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix AttributeError when wrapping a FileObject around already-opened text streams.
Fixes #1542
parent
3c624d73
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
83 additions
and
32 deletions
+83
-32
CHANGES.rst
CHANGES.rst
+3
-0
src/gevent/_fileobjectcommon.py
src/gevent/_fileobjectcommon.py
+45
-26
src/gevent/lock.py
src/gevent/lock.py
+2
-1
src/gevent/tests/test__fileobject.py
src/gevent/tests/test__fileobject.py
+32
-5
tox.ini
tox.ini
+1
-0
No files found.
CHANGES.rst
View file @
a208118f
...
@@ -8,6 +8,9 @@
...
@@ -8,6 +8,9 @@
==================
==================
- Make `gevent.lock.RLock.acquire` accept the *timeout* parameter.
- Make `gevent.lock.RLock.acquire` accept the *timeout* parameter.
- Fix an ``AttributeError`` when wrapping gevent's ``FileObject``
around an opened text stream. Reported in :issue:`1542` by
dmrlawson.
1.5a4 (2020-03-23)
1.5a4 (2020-03-23)
...
...
src/gevent/_fileobjectcommon.py
View file @
a208118f
...
@@ -222,8 +222,8 @@ class OpenDescriptor(object): # pylint:disable=too-many-instance-attributes
...
@@ -222,8 +222,8 @@ class OpenDescriptor(object): # pylint:disable=too-many-instance-attributes
def
wrapped
(
self
,
raw
):
def
wrapped
(
self
,
raw
):
"""
"""
Wraps the raw IO object (`RawIOBase`
) in buffers, text decoding,
Wraps the raw IO object (`RawIOBase`
or `io.TextIOBase`) in
and newline handling.
buffers, text decoding,
and newline handling.
"""
"""
# pylint:disable=too-many-branches
# pylint:disable=too-many-branches
result
=
raw
result
=
raw
...
@@ -245,40 +245,59 @@ class OpenDescriptor(object): # pylint:disable=too-many-instance-attributes
...
@@ -245,40 +245,59 @@ class OpenDescriptor(object): # pylint:disable=too-many-instance-attributes
if
buffering
<
0
:
# pragma: no cover
if
buffering
<
0
:
# pragma: no cover
raise
ValueError
(
"invalid buffering size"
)
raise
ValueError
(
"invalid buffering size"
)
if
buffering
!=
0
:
if
not
isinstance
(
raw
,
io
.
BufferedIOBase
)
and
\
if
self
.
updating
:
(
not
hasattr
(
raw
,
'buffer'
)
or
raw
.
buffer
is
None
):
Buffer
=
io
.
BufferedRandom
# Need to wrap our own buffering around it. If it
elif
self
.
creating
or
self
.
writing
or
self
.
appending
:
# is already buffered, don't do so.
Buffer
=
io
.
BufferedWriter
if
buffering
!=
0
:
elif
self
.
reading
:
if
self
.
updating
:
Buffer
=
io
.
BufferedReader
Buffer
=
io
.
BufferedRandom
else
:
# prgama: no cover
elif
self
.
creating
or
self
.
writing
or
self
.
appending
:
raise
ValueError
(
"unknown mode: %r"
%
self
.
mode
)
Buffer
=
io
.
BufferedWriter
elif
self
.
reading
:
try
:
Buffer
=
io
.
BufferedReader
result
=
Buffer
(
raw
,
buffering
)
else
:
# prgama: no cover
except
AttributeError
:
raise
ValueError
(
"unknown mode: %r"
%
self
.
mode
)
# Python 2 file() objects don't have the readable/writable
# attributes. But they handle their own buffering.
try
:
result
=
raw
result
=
Buffer
(
raw
,
buffering
)
except
AttributeError
:
# Python 2 file() objects don't have the readable/writable
# attributes. But they handle their own buffering.
result
=
raw
if
self
.
binary
:
if
self
.
binary
:
if
isinstance
(
raw
,
io
.
TextIOBase
):
# Can't do it. The TextIO object will have its own buffer, and
# trying to read from the raw stream or the buffer without going through
# the TextIO object is likely to lead to problems with the codec.
raise
ValueError
(
"Unable to perform binary IO on top of text IO stream"
)
return
result
return
result
# Either native or text at this point.
if
PY2
and
self
.
native
:
if
PY2
and
self
.
native
:
# Neither text mode nor binary mode specified.
# Neither text mode nor binary mode specified.
if
self
.
universal
:
if
self
.
universal
:
# universal was requested, e.g., 'rU'
# universal was requested, e.g., 'rU'
result
=
UniversalNewlineBytesWrapper
(
result
,
line_buffering
)
result
=
UniversalNewlineBytesWrapper
(
result
,
line_buffering
)
else
:
else
:
result
=
io
.
TextIOWrapper
(
result
,
self
.
encoding
,
self
.
errors
,
self
.
newline
,
# Python 2 and text mode, or Python 3 and either text or native (both are the same)
line_buffering
)
if
not
isinstance
(
raw
,
io
.
TextIOBase
):
# Avoid double-wrapping a TextIOBase in another TextIOWrapper.
# That tends not to work. See https://github.com/gevent/gevent/issues/1542
result
=
io
.
TextIOWrapper
(
result
,
self
.
encoding
,
self
.
errors
,
self
.
newline
,
line_buffering
)
if
result
is
not
raw
:
# Set the mode, if possible, but only if we created a new
# object.
try
:
result
.
mode
=
self
.
mode
except
(
AttributeError
,
TypeError
):
# AttributeError: No such attribute
# TypeError: Readonly attribute (py2)
pass
try
:
result
.
mode
=
self
.
mode
except
(
AttributeError
,
TypeError
):
# AttributeError: No such attribute
# TypeError: Readonly attribute (py2)
pass
return
result
return
result
...
...
src/gevent/lock.py
View file @
a208118f
...
@@ -188,8 +188,9 @@ class DummySemaphore(object):
...
@@ -188,8 +188,9 @@ class DummySemaphore(object):
def
unlink
(
self
,
callback
):
def
unlink
(
self
,
callback
):
pass
pass
def
wait
(
self
,
timeout
=
None
):
def
wait
(
self
,
timeout
=
None
):
# pylint:disable=unused-argument
"""Waiting for a DummySemaphore returns immediately."""
"""Waiting for a DummySemaphore returns immediately."""
return
1
def
acquire
(
self
,
blocking
=
True
,
timeout
=
None
):
def
acquire
(
self
,
blocking
=
True
,
timeout
=
None
):
"""
"""
...
...
src/gevent/tests/test__fileobject.py
View file @
a208118f
from
__future__
import
print_function
from
__future__
import
print_function
,
absolute_import
import
functools
import
functools
import
gc
import
gc
...
@@ -133,7 +133,8 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
...
@@ -133,7 +133,8 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
self
.
assertEqual
(
native_data
,
fileobj_data
)
self
.
assertEqual
(
native_data
,
fileobj_data
)
def
__check_native_matches
(
self
,
byte_data
,
open_mode
,
def
__check_native_matches
(
self
,
byte_data
,
open_mode
,
meth
=
'read'
,
**
open_kwargs
):
meth
=
'read'
,
open_path
=
True
,
**
open_kwargs
):
fileno
,
path
=
tempfile
.
mkstemp
(
'.gevent_test_'
+
open_mode
)
fileno
,
path
=
tempfile
.
mkstemp
(
'.gevent_test_'
+
open_mode
)
self
.
addCleanup
(
os
.
remove
,
path
)
self
.
addCleanup
(
os
.
remove
,
path
)
...
@@ -143,8 +144,16 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
...
@@ -143,8 +144,16 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
with
io
.
open
(
path
,
open_mode
,
**
open_kwargs
)
as
f
:
with
io
.
open
(
path
,
open_mode
,
**
open_kwargs
)
as
f
:
native_data
=
getattr
(
f
,
meth
)()
native_data
=
getattr
(
f
,
meth
)()
with
self
.
_makeOne
(
path
,
open_mode
,
**
open_kwargs
)
as
f
:
if
open_path
:
gevent_data
=
getattr
(
f
,
meth
)()
with
self
.
_makeOne
(
path
,
open_mode
,
**
open_kwargs
)
as
f
:
gevent_data
=
getattr
(
f
,
meth
)()
else
:
# Note that we don't use ``io.open()`` for the raw file,
# on Python 2. We want 'r' to mean what the usual call to open() means.
opener
=
io
.
open
if
PY3
else
open
with
opener
(
path
,
open_mode
,
**
open_kwargs
)
as
raw
:
with
self
.
_makeOne
(
raw
)
as
f
:
gevent_data
=
getattr
(
f
,
meth
)()
self
.
assertEqual
(
native_data
,
gevent_data
)
self
.
assertEqual
(
native_data
,
gevent_data
)
return
gevent_data
return
gevent_data
...
@@ -171,7 +180,7 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
...
@@ -171,7 +180,7 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
pass
pass
@
skipUnlessWorksWithRegularFiles
@
skipUnlessWorksWithRegularFiles
def
test_rbU_produces_bytes
(
self
):
def
test_rbU_produces_bytes
_readline
(
self
):
# Including U in rb still produces bytes.
# Including U in rb still produces bytes.
# Note that the universal newline behaviour is
# Note that the universal newline behaviour is
# essentially ignored in explicit bytes mode.
# essentially ignored in explicit bytes mode.
...
@@ -192,6 +201,24 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
...
@@ -192,6 +201,24 @@ class TestFileObjectBlock(greentest.TestCase): # serves as a base for the concur
)
)
self
.
assertIsInstance
(
gevent_data
[
0
],
str
)
self
.
assertIsInstance
(
gevent_data
[
0
],
str
)
@
skipUnlessWorksWithRegularFiles
def
test_r_readline_produces_native
(
self
):
gevent_data
=
self
.
__check_native_matches
(
b'line1
\
n
'
,
'r'
,
meth
=
'readline'
,
)
self
.
assertIsInstance
(
gevent_data
,
str
)
@
skipUnlessWorksWithRegularFiles
def
test_r_readline_on_fobject_produces_native
(
self
):
gevent_data
=
self
.
__check_native_matches
(
b'line1
\
n
'
,
'r'
,
meth
=
'readline'
,
open_path
=
False
,
)
self
.
assertIsInstance
(
gevent_data
,
str
)
def
test_close_pipe
(
self
):
def
test_close_pipe
(
self
):
# Issue #190, 203
# Issue #190, 203
...
...
tox.ini
View file @
a208118f
...
@@ -3,6 +3,7 @@ envlist =
...
@@ -3,6 +3,7 @@ envlist =
py27,py35,py36,py37,py27-cffi,pypy,pypy3,py27-libuv,lint
py27,py35,py36,py37,py27-cffi,pypy,pypy3,py27-libuv,lint
[testenv]
[testenv]
usedevelop
=
true
extras
=
extras
=
test
test
events
events
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment