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
ed147335
Commit
ed147335
authored
Dec 21, 2020
by
Jason Madden
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor _socket2/_socket3 to move more truly common methods into SocketMixin.
parent
8b0432b4
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
284 additions
and
364 deletions
+284
-364
.github/workflows/ci.yml
.github/workflows/ci.yml
+5
-1
docs/api/gevent._socket2.rst
docs/api/gevent._socket2.rst
+5
-0
docs/api/gevent._socket3.rst
docs/api/gevent._socket3.rst
+1
-0
docs/api/gevent.socket.rst
docs/api/gevent.socket.rst
+2
-0
docs/changes/1724.bugfix
docs/changes/1724.bugfix
+3
-0
src/gevent/_socket2.py
src/gevent/_socket2.py
+1
-178
src/gevent/_socket3.py
src/gevent/_socket3.py
+0
-184
src/gevent/_socketcommon.py
src/gevent/_socketcommon.py
+267
-1
No files found.
.github/workflows/ci.yml
View file @
ed147335
...
...
@@ -112,6 +112,10 @@ jobs:
-
name
:
Enable emulation
run
:
|
docker run --rm --privileged hypriot/qemu-register
# This one was seen in pyca/bcrypt. What's the difference?
# (Other than this one not working.)
#run: |
# docker run --rm --privileged multiarch/qemu-user-static:register --reset
-
name
:
Build and test gevent
# Skip the bulk of the tests, running them emulated on arm takes
# forever. Likewise, don't build or even configure c-ares; in the emulated platform
...
...
@@ -275,7 +279,7 @@ jobs:
python -c 'import gevent.ares; print(gevent.ares)'
ccache -s
-
name
:
Lint (Python 3.9)
if
:
${{ matrix.python-version == 3.9 }}
if
:
matrix.python-version == 3.9 && startsWith(runner.os, 'Linux')
# We only need to do this on one version, and it should be Python 3, because
# pylint has stopped updating for Python 2.
# We do this here rather than a separate job to avoid the compilation overhead.
...
...
docs/api/gevent._socket2.rst
View file @
ed147335
...
...
@@ -2,5 +2,10 @@
:mod:`gevent._socket2` -- Python 2 socket module
==================================================
.. caution:: Some of the docstrings are automatically generated and
may be correct for Python 3 but not Python 2. Please see
the standard library documentation.
.. automodule:: gevent._socket2
:members:
:inherited-members:
docs/api/gevent._socket3.rst
View file @
ed147335
...
...
@@ -4,3 +4,4 @@
.. automodule:: gevent._socket3
:members:
:inherited-members:
docs/api/gevent.socket.rst
View file @
ed147335
...
...
@@ -52,6 +52,8 @@ Python 2 and Python 3, respectively.
standard library sockets never have.
.. toctree::
Python 3 interface <gevent._socket3>
...
...
docs/changes/1724.bugfix
View file @
ed147335
...
...
@@ -2,3 +2,6 @@ Remove the ``__dict__`` attribute from `gevent.socket.socket` objects. The
standard library socket do not have a ``__dict__``.
Noticed by Carson Ip.
As part of this refactoring, share more common socket code between Python 2
and Python 3.
src/gevent/_socket2.py
View file @
ed147335
...
...
@@ -12,7 +12,6 @@ import sys
from
gevent
import
_socketcommon
from
gevent._util
import
copy_globals
from
gevent._compat
import
PYPY
from
gevent.timeout
import
Timeout
copy_globals
(
_socketcommon
,
globals
(),
names_to_ignore
=
_socketcommon
.
__py3_imports__
+
_socketcommon
.
__extensions__
,
...
...
@@ -25,17 +24,10 @@ __imports__ = [i for i in _socketcommon.__imports__ if i not in _socketcommon.__
__dns__
=
_socketcommon
.
__dns__
try
:
_fileobject
=
__socket__
.
_fileobject
_socketmethods
=
__socket__
.
_socketmethods
except
AttributeError
:
# Allow this module to be imported under Python 3
# for building the docs
_fileobject
=
object
_socketmethods
=
(
'bind'
,
'connect'
,
'connect_ex'
,
'fileno'
,
'listen'
,
'getpeername'
,
'getsockname'
,
'getsockopt'
,
'setsockopt'
,
'sendall'
,
'setblocking'
,
'settimeout'
,
'gettimeout'
,
'shutdown'
)
else
:
# Python 2 doesn't natively support with statements on _fileobject;
# but it substantially eases our test cases if we can do the same with on both Py3
...
...
@@ -74,8 +66,6 @@ else:
super
(
_fileobject
,
self
).
close
()
from
gevent._greenlet_primitives
import
get_memory
as
_get_memory
class
_closedsocket
(
object
):
__slots__
=
()
...
...
@@ -100,11 +90,8 @@ class _closedsocket(object):
__getattr__
=
_dummy
timeout_default
=
object
()
gtype
=
type
from
gevent._hub_primitives
import
wait_on_socket
as
_wait_on_socket
_Base
=
_socketcommon
.
SocketMixin
class
socket
(
_Base
):
...
...
@@ -189,17 +176,6 @@ class socket(_Base):
result
+=
' timeout='
+
str
(
self
.
timeout
)
return
result
def
_get_ref
(
self
):
return
self
.
_read_event
.
ref
or
self
.
_write_event
.
ref
def
_set_ref
(
self
,
value
):
self
.
_read_event
.
ref
=
value
self
.
_write_event
.
ref
=
value
ref
=
property
(
_get_ref
,
_set_ref
)
_wait
=
_wait_on_socket
def
accept
(
self
):
while
1
:
try
:
...
...
@@ -255,46 +231,6 @@ class socket(_Base):
def
closed
(
self
):
return
isinstance
(
self
.
_sock
,
_closedsocket
)
def
connect
(
self
,
address
):
"""
Connect to *address*.
.. versionchanged:: 20.6.0
If the host part of the address includes an IPv6 scope ID,
it will be used instead of ignored, if the platform supplies
:func:`socket.inet_pton`.
"""
if
self
.
timeout
==
0.0
:
return
self
.
_sock
.
connect
(
address
)
address
=
_socketcommon
.
_resolve_addr
(
self
.
_sock
,
address
)
timer
=
Timeout
.
_start_new_or_dummy
(
self
.
timeout
,
timeout
(
'timed out'
))
try
:
while
1
:
err
=
self
.
_sock
.
getsockopt
(
SOL_SOCKET
,
SO_ERROR
)
if
err
:
raise
error
(
err
,
strerror
(
err
))
result
=
self
.
_sock
.
connect_ex
(
address
)
if
not
result
or
result
==
EISCONN
:
break
if
(
result
in
(
EWOULDBLOCK
,
EINPROGRESS
,
EALREADY
))
or
(
result
==
EINVAL
and
is_windows
):
self
.
_wait
(
self
.
_write_event
)
else
:
raise
error
(
result
,
strerror
(
result
))
finally
:
timer
.
close
()
def
connect_ex
(
self
,
address
):
try
:
return
self
.
connect
(
address
)
or
0
except
timeout
:
return
EAGAIN
except
error
as
ex
:
if
type
(
ex
)
is
error
:
# pylint:disable=unidiomatic-typecheck
return
ex
.
args
[
0
]
raise
# gaierror is not silenced by connect_ex
def
dup
(
self
):
"""dup() -> socket object
...
...
@@ -321,123 +257,10 @@ class socket(_Base):
self
.
_sock
.
_drop
()
return
fobj
def
recv
(
self
,
*
args
):
while
1
:
try
:
return
self
.
_sock
.
recv
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
# QQQ without clearing exc_info test__refcount.test_clean_exit fails
sys
.
exc_clear
()
self
.
_wait
(
self
.
_read_event
)
def
recvfrom
(
self
,
*
args
):
while
1
:
try
:
return
self
.
_sock
.
recvfrom
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
sys
.
exc_clear
()
self
.
_wait
(
self
.
_read_event
)
def
recvfrom_into
(
self
,
*
args
):
while
1
:
try
:
return
self
.
_sock
.
recvfrom_into
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
sys
.
exc_clear
()
self
.
_wait
(
self
.
_read_event
)
def
recv_into
(
self
,
*
args
):
while
1
:
try
:
return
self
.
_sock
.
recv_into
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
sys
.
exc_clear
()
self
.
_wait
(
self
.
_read_event
)
def
send
(
self
,
data
,
flags
=
0
,
timeout
=
timeout_default
):
if
timeout
is
timeout_default
:
timeout
=
self
.
timeout
try
:
return
self
.
_sock
.
send
(
data
,
flags
)
except
error
as
ex
:
if
ex
.
args
[
0
]
not
in
_socketcommon
.
GSENDAGAIN
or
timeout
==
0.0
:
raise
sys
.
exc_clear
()
self
.
_wait
(
self
.
_write_event
)
try
:
return
self
.
_sock
.
send
(
data
,
flags
)
except
error
as
ex2
:
if
ex2
.
args
[
0
]
==
EWOULDBLOCK
:
return
0
raise
def
sendall
(
self
,
data
,
flags
=
0
):
if
isinstance
(
data
,
unicode
):
data
=
data
.
encode
()
# this sendall is also reused by gevent.ssl.SSLSocket subclass,
# so it should not call self._sock methods directly
data_memory
=
_get_memory
(
data
)
return
_socketcommon
.
_sendall
(
self
,
data_memory
,
flags
)
def
sendto
(
self
,
*
args
):
try
:
return
self
.
_sock
.
sendto
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
sys
.
exc_clear
()
self
.
_wait
(
self
.
_write_event
)
try
:
return
self
.
_sock
.
sendto
(
*
args
)
except
error
as
ex2
:
if
ex2
.
args
[
0
]
==
EWOULDBLOCK
:
return
0
raise
def
setblocking
(
self
,
flag
):
if
flag
:
self
.
timeout
=
None
else
:
self
.
timeout
=
0.0
def
shutdown
(
self
,
how
):
if
how
==
0
:
# SHUT_RD
self
.
hub
.
cancel_wait
(
self
.
_read_event
,
cancel_wait_ex
)
elif
how
==
1
:
# SHUT_WR
self
.
hub
.
cancel_wait
(
self
.
_write_event
,
cancel_wait_ex
)
else
:
self
.
hub
.
cancel_wait
(
self
.
_read_event
,
cancel_wait_ex
)
self
.
hub
.
cancel_wait
(
self
.
_write_event
,
cancel_wait_ex
)
self
.
_sock
.
shutdown
(
how
)
family
=
property
(
lambda
self
:
self
.
_sock
.
family
)
type
=
property
(
lambda
self
:
self
.
_sock
.
type
)
proto
=
property
(
lambda
self
:
self
.
_sock
.
proto
)
def
fileno
(
self
):
return
self
.
_sock
.
fileno
()
def
getsockname
(
self
):
return
self
.
_sock
.
getsockname
()
def
getpeername
(
self
):
return
self
.
_sock
.
getpeername
()
# delegate the functions that we haven't implemented to the real socket object
_s
=
"def %s(self, *args): return self._sock.%s(*args)
\
n
\
n
"
_m
=
None
for
_m
in
set
(
_socketmethods
)
-
set
(
locals
())
-
{
'settimeout'
,
'gettimeout'
}:
exec
(
_s
%
(
_m
,
_m
,))
del
_m
,
_s
return
_Base
.
sendall
(
self
,
data
,
flags
)
if
PYPY
:
...
...
src/gevent/_socket3.py
View file @
ed147335
...
...
@@ -14,7 +14,6 @@ import sys
from
gevent
import
_socketcommon
from
gevent._util
import
copy_globals
from
gevent._compat
import
PYPY
from
gevent.timeout
import
Timeout
import
_socket
from
os
import
dup
...
...
@@ -23,13 +22,6 @@ copy_globals(_socketcommon, globals(),
names_to_ignore
=
_socketcommon
.
__extensions__
,
dunder_names_to_keep
=
())
try
:
from
errno
import
EHOSTUNREACH
from
errno
import
ECONNREFUSED
except
ImportError
:
EHOSTUNREACH
=
-
1
ECONNREFUSED
=
-
1
__socket__
=
_socketcommon
.
__socket__
__implements__
=
_socketcommon
.
_implements
...
...
@@ -40,9 +32,6 @@ __dns__ = _socketcommon.__dns__
SocketIO
=
__socket__
.
SocketIO
# pylint:disable=no-member
from
gevent._greenlet_primitives
import
get_memory
as
_get_memory
timeout_default
=
object
()
class
_closedsocket
(
object
):
__slots__
=
(
'family'
,
'type'
,
'proto'
,
'orig_fileno'
,
'description'
)
...
...
@@ -96,7 +85,6 @@ class _wrefsocket(_socket.socket):
timeout
=
property
(
lambda
s
:
s
.
gettimeout
(),
lambda
s
,
nv
:
s
.
settimeout
(
nv
))
from
gevent._hub_primitives
import
wait_on_socket
as
_wait_on_socket
class
socket
(
_socketcommon
.
SocketMixin
):
"""
...
...
@@ -168,16 +156,6 @@ class socket(_socketcommon.SocketMixin):
return
self
.
_sock
.
type
&
~
_socket
.
SOCK_NONBLOCK
# pylint:disable=no-member
return
self
.
_sock
.
type
def
getblocking
(
self
):
"""
Returns whether the socket will approximate blocking
behaviour.
.. versionadded:: 1.3a2
Added in Python 3.7.
"""
return
self
.
timeout
!=
0.0
def
__enter__
(
self
):
return
self
...
...
@@ -212,17 +190,6 @@ class socket(_socketcommon.SocketMixin):
def
__getstate__
(
self
):
raise
TypeError
(
"Cannot serialize socket object"
)
def
_get_ref
(
self
):
return
self
.
_read_event
.
ref
or
self
.
_write_event
.
ref
def
_set_ref
(
self
,
value
):
self
.
_read_event
.
ref
=
value
self
.
_write_event
.
ref
=
value
ref
=
property
(
_get_ref
,
_set_ref
)
_wait
=
_wait_on_socket
def
dup
(
self
):
"""dup() -> socket object
...
...
@@ -392,71 +359,6 @@ class socket(_socketcommon.SocketMixin):
self
.
_detach_socket
(
'detached'
)
return
sock
.
detach
()
def
connect
(
self
,
address
):
"""
Connect to *address*.
.. versionchanged:: 20.6.0
If the host part of the address includes an IPv6 scope ID,
it will be used instead of ignored, if the platform supplies
:func:`socket.inet_pton`.
"""
if
self
.
timeout
==
0.0
:
return
_socket
.
socket
.
connect
(
self
.
_sock
,
address
)
address
=
_socketcommon
.
_resolve_addr
(
self
.
_sock
,
address
)
with
Timeout
.
_start_new_or_dummy
(
self
.
timeout
,
timeout
(
"timed out"
)):
while
True
:
err
=
self
.
getsockopt
(
SOL_SOCKET
,
SO_ERROR
)
if
err
:
raise
error
(
err
,
strerror
(
err
))
result
=
_socket
.
socket
.
connect_ex
(
self
.
_sock
,
address
)
if
not
result
or
result
==
EISCONN
:
break
if
(
result
in
(
EWOULDBLOCK
,
EINPROGRESS
,
EALREADY
))
or
(
result
==
EINVAL
and
is_windows
):
self
.
_wait
(
self
.
_write_event
)
else
:
if
(
isinstance
(
address
,
tuple
)
and
address
[
0
]
==
'fe80::1'
and
result
==
EHOSTUNREACH
):
# On Python 3.7 on mac, we see EHOSTUNREACH
# returned for this link-local address, but it really is
# supposed to be ECONNREFUSED according to the standard library
# tests (test_socket.NetworkConnectionNoServer.test_create_connection)
# (On previous versions, that code passed the '127.0.0.1' IPv4 address, so
# ipv6 link locals were never a factor; 3.7 passes 'localhost'.)
# It is something of a mystery how the stdlib socket code doesn't
# produce EHOSTUNREACH---I (JAM) can't see how socketmodule.c would avoid
# that. The normal connect just calls connect_ex much like we do.
result
=
ECONNREFUSED
raise
error
(
result
,
strerror
(
result
))
def
connect_ex
(
self
,
address
):
try
:
return
self
.
connect
(
address
)
or
0
except
timeout
:
return
EAGAIN
except
gaierror
:
# pylint:disable=try-except-raise
# gaierror/overflowerror/typerror is not silenced by connect_ex;
# gaierror extends OSError (aka error) so catch it first
raise
except
error
as
ex
:
# error is now OSError and it has various subclasses.
# Only those that apply to actually connecting are silenced by
# connect_ex.
if
ex
.
errno
:
return
ex
.
errno
raise
# pragma: no cover
def
recv
(
self
,
*
args
):
while
True
:
try
:
return
self
.
_sock
.
recv
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
self
.
_wait
(
self
.
_read_event
)
if
hasattr
(
_socket
.
socket
,
'recvmsg'
):
# Only on Unix; PyPy 3.5 5.10.0 provides sendmsg and recvmsg, but not
# recvmsg_into (at least on os x)
...
...
@@ -485,72 +387,6 @@ class socket(_socketcommon.SocketMixin):
raise
self
.
_wait
(
self
.
_read_event
)
def
recvfrom
(
self
,
*
args
):
while
True
:
try
:
return
self
.
_sock
.
recvfrom
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
self
.
_wait
(
self
.
_read_event
)
def
recvfrom_into
(
self
,
*
args
):
while
True
:
try
:
return
self
.
_sock
.
recvfrom_into
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
self
.
_wait
(
self
.
_read_event
)
def
recv_into
(
self
,
*
args
):
while
True
:
try
:
return
self
.
_sock
.
recv_into
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
self
.
_wait
(
self
.
_read_event
)
def
send
(
self
,
data
,
flags
=
0
,
timeout
=
timeout_default
):
if
timeout
is
timeout_default
:
timeout
=
self
.
timeout
try
:
return
self
.
_sock
.
send
(
data
,
flags
)
except
error
as
ex
:
if
ex
.
args
[
0
]
not
in
_socketcommon
.
GSENDAGAIN
or
timeout
==
0.0
:
raise
self
.
_wait
(
self
.
_write_event
)
try
:
return
_socket
.
socket
.
send
(
self
.
_sock
,
data
,
flags
)
except
error
as
ex2
:
if
ex2
.
args
[
0
]
==
EWOULDBLOCK
:
return
0
raise
def
sendall
(
self
,
data
,
flags
=
0
):
# XXX Now that we run on PyPy3, see the notes in _socket2.py's sendall()
# and implement that here if needed.
# PyPy3 is not optimized for performance yet, and is known to be slower than
# PyPy2, so it's possibly premature to do this. However, there is a 3.5 test case that
# possibly exposes this in a severe way.
data_memory
=
_get_memory
(
data
)
return
_socketcommon
.
_sendall
(
self
,
data_memory
,
flags
)
def
sendto
(
self
,
*
args
):
try
:
return
self
.
_sock
.
sendto
(
*
args
)
except
error
as
ex
:
if
ex
.
args
[
0
]
!=
EWOULDBLOCK
or
self
.
timeout
==
0.0
:
raise
self
.
_wait
(
self
.
_write_event
)
try
:
return
self
.
_sock
.
sendto
(
*
args
)
except
error
as
ex2
:
if
ex2
.
args
[
0
]
==
EWOULDBLOCK
:
return
0
raise
if
hasattr
(
_socket
.
socket
,
'sendmsg'
):
# Only on Unix
def
sendmsg
(
self
,
buffers
,
ancdata
=
(),
flags
=
0
,
address
=
None
):
...
...
@@ -572,26 +408,6 @@ class socket(_socketcommon.SocketMixin):
return
0
raise
def
setblocking
(
self
,
flag
):
# Beginning in 3.6.0b3 this is supposed to raise
# if the file descriptor is closed, but the test for it
# involves closing the fileno directly. Since we
# don't touch the fileno here, it doesn't make sense for
# us.
if
flag
:
self
.
timeout
=
None
else
:
self
.
timeout
=
0.0
def
shutdown
(
self
,
how
):
if
how
==
0
:
# SHUT_RD
self
.
hub
.
cancel_wait
(
self
.
_read_event
,
cancel_wait_ex
)
elif
how
==
1
:
# SHUT_WR
self
.
hub
.
cancel_wait
(
self
.
_write_event
,
cancel_wait_ex
)
else
:
self
.
hub
.
cancel_wait
(
self
.
_read_event
,
cancel_wait_ex
)
self
.
hub
.
cancel_wait
(
self
.
_write_event
,
cancel_wait_ex
)
self
.
_sock
.
shutdown
(
how
)
# sendfile: new in 3.5. But there's no real reason to not
# support it everywhere. Note that we can't use os.sendfile()
...
...
src/gevent/_socketcommon.py
View file @
ed147335
This diff is collapsed.
Click to expand it.
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