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
a74ba95c
Commit
a74ba95c
authored
Oct 31, 2022
by
Jason Madden
Committed by
GitHub
Oct 31, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1922 from gevent/issue-1909
Update to greenlet 2.0
parents
a0708588
ed7c1d8b
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
165 additions
and
77 deletions
+165
-77
.github/workflows/ci.yml
.github/workflows/ci.yml
+2
-2
appveyor.yml
appveyor.yml
+20
-4
deps/greenlet/greenlet.h
deps/greenlet/greenlet.h
+45
-42
docs/changes/1909.bugfix
docs/changes/1909.bugfix
+9
-0
pyproject.toml
pyproject.toml
+4
-2
scripts/releases/make-manylinux
scripts/releases/make-manylinux
+1
-1
setup.py
setup.py
+3
-1
src/gevent/_gevent_cgreenlet.pxd
src/gevent/_gevent_cgreenlet.pxd
+22
-5
src/gevent/subprocess.py
src/gevent/subprocess.py
+1
-0
src/gevent/tests/test__memleak.py
src/gevent/tests/test__memleak.py
+10
-0
src/gevent/tests/test__socket_dns.py
src/gevent/tests/test__socket_dns.py
+46
-18
src/gevent/tests/test__socket_dns6.py
src/gevent/tests/test__socket_dns6.py
+2
-2
No files found.
.github/workflows/ci.yml
View file @
a74ba95c
...
...
@@ -198,7 +198,7 @@ jobs:
pip install -q -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"'
pip install -q -U 'cffi;platform_python_implementation=="CPython"'
pip install -q -U 'cython>=3.0a9'
pip install 'greenlet>=
1.0a1,<2
;platform_python_implementation=="CPython"'
pip install 'greenlet>=
2.0rc4
;platform_python_implementation=="CPython"'
-
name
:
Build gevent
run
:
|
...
...
@@ -393,7 +393,7 @@ jobs:
pip install -q -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"'
pip install -q -U 'cffi;platform_python_implementation=="CPython"'
pip install -q -U 'cython>=3.0a5'
pip install 'greenlet>=
1.0a1,<2
;platform_python_implementation=="CPython"'
pip install 'greenlet>=
2.0rc4
;platform_python_implementation=="CPython"'
-
name
:
build libs and gevent
env
:
...
...
appveyor.yml
View file @
a74ba95c
...
...
@@ -46,6 +46,12 @@ environment:
# a later point release.
# 64-bit
-
PYTHON
:
"
C:
\\
Python311-x64"
PYTHON_VERSION
:
"
3.11.0"
PYTHON_ARCH
:
"
64"
PYTHON_EXE
:
python
APPVEYOR_BUILD_WORKER_IMAGE
:
Visual Studio
2019
-
PYTHON
:
"
C:
\\
pypy3.7-v7.3.7-win64"
PYTHON_ID
:
"
pypy3"
PYTHON_EXE
:
pypy3w
...
...
@@ -81,10 +87,6 @@ environment:
PYTHON_EXE
:
python
APPVEYOR_BUILD_WORKER_IMAGE
:
Visual Studio
2019
-
PYTHON
:
"
C:
\\
Python27-x64"
PYTHON_VERSION
:
"
2.7.x"
# currently 2.7.13
PYTHON_ARCH
:
"
64"
PYTHON_EXE
:
python
-
PYTHON
:
"
C:
\\
Python38-x64"
PYTHON_VERSION
:
"
3.8.x"
...
...
@@ -138,6 +140,20 @@ environment:
PYTHON_EXE
:
python
GWHEEL_ONLY
:
true
-
PYTHON
:
"
C:
\\
Python27-x64"
PYTHON_VERSION
:
"
2.7.x"
# currently 2.7.13
PYTHON_ARCH
:
"
64"
PYTHON_EXE
:
python
# greenlet 2.0 is evincing a warning (probably?)
# on shutdown, leading to the dreaded error:
# Fatal Python error: PyImport_GetModuleDict: no module
# dictionary!
# in some tests. This is hard to debug remotely, and as support
# for 2.7 is winding down quickly (hey, we're only two years
# late to the party) I'm not specifically going to try to debug
# it. We'll just provide a binary wheel still.
GWHEEL_ONLY
:
true
# Also test a Python version not pre-installed
# See: https://github.com/ogrisel/python-appveyor-demo/issues/10
...
...
deps/greenlet/greenlet.h
View file @
a74ba95c
...
...
@@ -5,6 +5,7 @@
#ifndef Py_GREENLETOBJECT_H
#define Py_GREENLETOBJECT_H
#include <Python.h>
#ifdef __cplusplus
...
...
@@ -14,60 +15,24 @@ extern "C" {
/* This is deprecated and undocumented. It does not change. */
#define GREENLET_VERSION "1.0.0"
#if PY_VERSION_HEX >= 0x30B00A6
# define GREENLET_PY311 1
/* _PyInterpreterFrame moved to the internal C API in Python 3.11 */
# include <internal/pycore_frame.h>
#else
# define GREENLET_PY311 0
# define _PyCFrame CFrame
#ifndef GREENLET_MODULE
#define implementation_ptr_t void*
#endif
typedef
struct
_greenlet
{
PyObject_HEAD
char
*
stack_start
;
char
*
stack_stop
;
char
*
stack_copy
;
intptr_t
stack_saved
;
struct
_greenlet
*
stack_prev
;
struct
_greenlet
*
parent
;
PyObject
*
run_info
;
struct
_frame
*
top_frame
;
int
recursion_depth
;
#if GREENLET_PY311
_PyInterpreterFrame
*
current_frame
;
_PyStackChunk
*
datastack_chunk
;
PyObject
**
datastack_top
;
PyObject
**
datastack_limit
;
#endif
PyObject
*
weakreflist
;
#if PY_VERSION_HEX >= 0x030700A3
_PyErr_StackItem
*
exc_info
;
_PyErr_StackItem
exc_state
;
#else
PyObject
*
exc_type
;
PyObject
*
exc_value
;
PyObject
*
exc_traceback
;
#endif
PyObject
*
dict
;
#if PY_VERSION_HEX >= 0x030700A3
PyObject
*
context
;
#endif
#if PY_VERSION_HEX >= 0x30A00B1
_PyCFrame
*
cframe
;
#endif
implementation_ptr_t
pimpl
;
}
PyGreenlet
;
#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type)
#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*)-1)
#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL)
#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL)
#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent)
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
/* C API functions */
/* Total number of symbols that are exported */
#define PyGreenlet_API_pointers
8
#define PyGreenlet_API_pointers
12
#define PyGreenlet_Type_NUM 0
#define PyExc_GreenletError_NUM 1
...
...
@@ -79,6 +44,11 @@ typedef struct _greenlet {
#define PyGreenlet_Switch_NUM 6
#define PyGreenlet_SetParent_NUM 7
#define PyGreenlet_MAIN_NUM 8
#define PyGreenlet_STARTED_NUM 9
#define PyGreenlet_ACTIVE_NUM 10
#define PyGreenlet_GET_PARENT_NUM 11
#ifndef GREENLET_MODULE
/* This section is used by modules that uses the greenlet C API */
static
void
**
_PyGreenlet_API
=
NULL
;
...
...
@@ -144,6 +114,39 @@ static void** _PyGreenlet_API = NULL;
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
/*
* PyGreenlet_GetParent(PyObject* greenlet)
*
* return greenlet.parent;
*
* This could return NULL even if there is no exception active.
* If it does not return NULL, you are responsible for decrementing the
* reference count.
*/
# define PyGreenlet_GetParent \
(*(PyGreenlet* (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
/*
* deprecated, undocumented alias.
*/
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
# define PyGreenlet_MAIN \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
# define PyGreenlet_STARTED \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
# define PyGreenlet_ACTIVE \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
/* Macro that imports greenlet and initializes C API */
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
keep the older definition to be sure older code that might have a copy of
...
...
docs/changes/1909.bugfix
0 → 100644
View file @
a74ba95c
Update to greenlet 2.0. This fixes a deallocation issue that required
a change in greenlet's ABI. The design of greenlet 2.0 is intended to
prevent future fixes and enhancements from requiring an ABI change,
making it easier to update gevent and greenlet independently.
.. caution::
greenlet 2.0 requires a modern-ish C++ compiler. This may mean
certain older platforms are no longer supported.
pyproject.toml
View file @
a74ba95c
...
...
@@ -23,8 +23,10 @@ requires = [
# Python 3.7 requires at least 0.4.14, which is ABI incompatible with earlier
# releases. Python 3.9 and 3.10 require 0.4.16;
# 0.4.17 is ABI incompatible with earlier releases, but compatible with 1.0
# 1.1.3 is needed for CPython 3.11
"greenlet >= 1.1.3, < 2.0 ; platform_python_implementation == 'CPython'"
,
# 1.1.3 is needed for CPython 3.11.
# 2.0 is not ABI compatible with earlier releases, but with luck it won't
# have to break the ABI again.
"greenlet >= 2.0.0rc4 ; platform_python_implementation == 'CPython'"
,
]
[tool.towncrier]
...
...
scripts/releases/make-manylinux
View file @
a74ba95c
...
...
@@ -125,7 +125,7 @@ if [ -d /gevent -a -d /opt/python ]; then
# The downside is that we must install dependencies manually.
# NOTE: We can't upgrade ``wheel`` because ``auditwheel`` depends on
# it, and auditwheel is installed in one of these environments.
python
-mpip
install
-U
"cython >= 3.0a6"
cffi
'greenlet >=
1.0
'
setuptools
python
-mpip
install
-U
"cython >= 3.0a6"
cffi
'greenlet >=
2.0rc4
'
setuptools
time
(
python setup.py bdist_wheel
)
PATH
=
"
$OPATH
"
auditwheel repair dist/gevent
*
.whl
cp
wheelhouse/gevent
*
.whl /gevent/wheelhouse
...
...
setup.py
View file @
a74ba95c
...
...
@@ -213,7 +213,9 @@ greenlet_requires = [
# so we can add an upper bound).
# 1.1.0 is required for 3.10; it has a new ABI, but only on 1.1.0.
# 1.1.3 is needed for 3.11, and supports everything 1.1.0 did.
'greenlet >= 1.1.3, < 2.0; platform_python_implementation=="CPython"'
,
# 2.0.0 supports everything 1.1.3 did, but breaks the ABI in a way that hopefully
# won't break again.
'greenlet >= 2.0.0rc4 ; platform_python_implementation=="CPython"'
,
]
# Note that we don't add cffi to install_requires, it's
...
...
src/gevent/_gevent_cgreenlet.pxd
View file @
a74ba95c
# cython: auto_pickle=False
cimport
cython
from
cpython.ref
cimport
Py_DECREF
from
gevent._gevent_c_ident
cimport
IdentRegistry
from
gevent._gevent_c_hub_local
cimport
get_hub_noargs
as
get_hub
from
gevent._gevent_c_waiter
cimport
Waiter
...
...
@@ -21,12 +23,14 @@ cdef extern from "greenlet/greenlet.h":
# properly handle the case that it can be NULL. So instead we inline a getparent
# function that does the same thing as the green_getparent accessor but without
# going through the overhead of generic attribute lookup.
cdef
void
*
parent
#cdef void* parent
pass
# These are actually macros and so must be included
# (defined) in each .pxd, as are the two functions
# that call them.
greenlet
PyGreenlet_GetCurrent
()
void
*
PyGreenlet_GetParent
(
greenlet
)
void
PyGreenlet_Import
()
@
cython
.
final
...
...
@@ -36,13 +40,26 @@ cdef inline greenlet getcurrent():
cdef
inline
object
get_generic_parent
(
greenlet
s
):
# We don't use any typed functions on the return of this,
# so save the type check by making it just an object.
if
s
.
parent
!=
NULL
:
return
<
object
>
s
.
parent
cdef
object
result
cdef
void
*
parent
=
PyGreenlet_GetParent
(
s
)
if
parent
!=
NULL
:
# The cast will perform an incref; but the GetParent
# function already did an incref if we got it (and not NULL).
# Therefore, we must DECREF immediately.
result
=
<
object
>
parent
Py_DECREF
(
result
)
return
result
cdef
inline
SwitchOutGreenletWithLoop
get_my_hub
(
greenlet
s
):
# This one we do want type checked on the return value.
# Must not be called with s = None
if
s
.
parent
!=
NULL
:
return
<
object
>
s
.
parent
cdef
object
result
cdef
void
*
parent
=
PyGreenlet_GetParent
(
s
)
if
parent
!=
NULL
:
result
=
<
object
>
parent
# See above
Py_DECREF
(
result
)
return
result
cdef
bint
_greenlet_imported
...
...
src/gevent/subprocess.py
View file @
a74ba95c
...
...
@@ -224,6 +224,7 @@ if PY311:
_fork_exec
=
None
__implements__
.
extend
([
'_fork_exec'
,
]
if
sys
.
platform
!=
'win32'
else
[
])
actually_imported
=
copy_globals
(
__subprocess__
,
globals
(),
...
...
src/gevent/tests/test__memleak.py
View file @
a74ba95c
...
...
@@ -26,6 +26,16 @@ class TestQueue(TestCase): # pragma: no cover
refcounts
.
append
(
sys
.
gettotalrefcount
())
# Refcounts may go down, but not up
# XXX: JAM: I think this may just be broken. Each time we add
# a new integer to our list of refcounts, we'll be
# creating a new reference. This makes sense when we see the list
# go up by one each iteration:
#
# AssertionError: 530631 not less than or equal to 530630
# : total refcount mismatch:
# [530381, 530618, 530619, 530620, 530621,
# 530622, 530623, 530624, 530625, 530626,
# 530627, 530628, 530629, 530630, 530631]
final
=
refcounts
[
-
1
]
previous
=
refcounts
[
-
2
]
self
.
assertLessEqual
(
...
...
src/gevent/tests/test__socket_dns.py
View file @
a74ba95c
...
...
@@ -34,6 +34,7 @@ from gevent.testing.sysinfo import RESOLVER_DNSPYTHON
from
gevent.testing.sysinfo
import
RESOLVER_ARES
from
gevent.testing.sysinfo
import
PY2
from
gevent.testing.sysinfo
import
PYPY
import
gevent.testing.timing
...
...
@@ -45,7 +46,8 @@ RUN_ALL_HOST_TESTS = os.getenv('GEVENTTEST_RUN_ALL_ETC_HOST_TESTS', '')
def
add
(
klass
,
hostname
,
name
=
None
,
skip
=
None
,
skip_reason
=
None
):
skip
=
None
,
skip_reason
=
None
,
require_equal_errors
=
True
):
call
=
callable
(
hostname
)
...
...
@@ -64,33 +66,39 @@ def add(klass, hostname, name=None,
def test_getaddrinfo_http(self):
x = hostname() if call else hostname
self._test('
getaddrinfo
', x, '
http
')
self._test('
getaddrinfo
', x, '
http
',
require_equal_errors=require_equal_errors)
test_getaddrinfo_http.__name__ = '
test_
%
s_getaddrinfo_http
' % name
_setattr(klass, test_getaddrinfo_http.__name__, test_getaddrinfo_http)
def test_gethostbyname(self):
x = hostname() if call else hostname
ipaddr = self._test('
gethostbyname
', x)
ipaddr = self._test('
gethostbyname
', x,
require_equal_errors=require_equal_errors)
if not isinstance(ipaddr, Exception):
self._test('
gethostbyaddr
', ipaddr)
self._test('
gethostbyaddr
', ipaddr,
require_equal_errors=require_equal_errors)
test_gethostbyname.__name__ = '
test_
%
s_gethostbyname
' % name
_setattr(klass, test_gethostbyname.__name__, test_gethostbyname)
def test
3
(self):
def test
_gethostbyname_ex
(self):
x = hostname() if call else hostname
self._test('
gethostbyname_ex
', x)
test3.__name__ = '
test_
%
s_gethostbyname_ex
' % name
_setattr(klass, test3.__name__, test3)
self._test('
gethostbyname_ex
', x,
require_equal_errors=require_equal_errors)
test_gethostbyname_ex.__name__ = '
test_
%
s_gethostbyname_ex
' % name
_setattr(klass, test_gethostbyname_ex.__name__, test_gethostbyname_ex)
def test4(self):
x = hostname() if call else hostname
self._test('
gethostbyaddr
', x)
self._test('
gethostbyaddr
', x,
require_equal_errors=require_equal_errors)
test4.__name__ = '
test_
%
s_gethostbyaddr
' % name
_setattr(klass, test4.__name__, test4)
def test5(self):
x = hostname() if call else hostname
self._test('
getnameinfo
', (x, 80), 0)
self._test('
getnameinfo
', (x, 80), 0,
require_equal_errors=require_equal_errors)
test5.__name__ = '
test_
%
s_getnameinfo
' % name
_setattr(klass, test5.__name__, test5)
...
...
@@ -187,17 +195,20 @@ class TestCase(greentest.TestCase):
return type(result1) is not type(result2)
return repr(result1) != repr(result2)
def _test(self, func_name, *args):
def _test(self, func_name, *args
, **kwargs
):
"""
Runs the function *func_name* with *args* and compares gevent and the system.
Keyword arguments are passed to the function itself; variable args are
used for the socket function.
Returns the gevent result.
"""
gevent_func = getattr(gevent_socket, func_name)
real_func = monkey.get_original('
socket
', func_name)
tester = getattr(self, '
_run_test_
' + func_name, self._run_test_generic)
result = tester(func_name, real_func, gevent_func, args)
result = tester(func_name, real_func, gevent_func, args
, **kwargs
)
_real_result, time_real, gevent_result, time_gevent = result
if self.verbose_dns and time_gevent > time_real + 0.02 and time_gevent > 0.03:
...
...
@@ -213,14 +224,17 @@ class TestCase(greentest.TestCase):
return gevent_result
def _run_test_generic(self, func_name, real_func, gevent_func, func_args):
def _run_test_generic(self, func_name, real_func, gevent_func, func_args,
require_equal_errors=True):
real_result, time_real = self.run_resolver(real_func, func_args)
gevent_result, time_gevent = self.run_resolver(gevent_func, func_args)
if util.QUIET and self.should_log_results(real_result, gevent_result):
util.log('')
self.__trace_call(real_result, time_real, real_func, func_args)
self.__trace_call(gevent_result, time_gevent, gevent_func, func_args)
self.assertEqualResults(real_result, gevent_result, func_name)
self.assertEqualResults(real_result, gevent_result, func_name,
require_equal_errors=require_equal_errors)
return real_result, time_real, gevent_result, time_gevent
def _normalize_result(self, result, func_name):
...
...
@@ -411,7 +425,8 @@ class TestCase(greentest.TestCase):
# As for getaddrinfo, we'll just check the ipaddrlist has something in common.
return
not
set
(
real_result
[
2
]).
isdisjoint
(
set
(
gevent_result
[
2
]))
def
assertEqualResults
(
self
,
real_result
,
gevent_result
,
func_name
):
def
assertEqualResults
(
self
,
real_result
,
gevent_result
,
func_name
,
require_equal_errors
=
True
):
errors
=
(
OverflowError
,
TypeError
,
...
...
@@ -421,7 +436,8 @@ class TestCase(greentest.TestCase):
socket
.
herror
,
)
if
isinstance
(
real_result
,
errors
)
and
isinstance
(
gevent_result
,
errors
):
self
.
_compare_exceptions
(
real_result
,
gevent_result
,
func_name
)
if
require_equal_errors
:
self
.
_compare_exceptions
(
real_result
,
gevent_result
,
func_name
)
return
real_result
=
self
.
_normalize_result
(
real_result
,
func_name
)
...
...
@@ -769,10 +785,22 @@ class TestInternational(TestCase):
# the 2008 version of idna encoding, whereas on Python 2,
# with the default resolver, it tries to encode to ascii and
# raises a UnicodeEncodeError. So we get different results.
# Starting 20221027, on GitHub Actions and *some* versions of Python,
# we started getting a different error result from our own resolver
# compared to the system. This is very weird because our own resolver
# calls the system. I can't reproduce locally. Perhaps the two
# different answers are because of caching? One from the real DNS
# server, one from the local resolver library? Hence
# require_equal_errors=False
# ('system:', "herror(2, 'Host name lookup failure')",
# 'gevent:', "herror(1, 'Unknown host')")
add
(
TestInternational
,
u'президент.рф'
,
'russian'
,
skip
=
(
PY2
and
RESOLVER_DNSPYTHON
),
skip_reason
=
"dnspython can actually resolve these"
)
add
(
TestInternational
,
u'президент.рф'
.
encode
(
'idna'
),
'idna'
)
skip_reason
=
"dnspython can actually resolve these"
,
require_equal_errors
=
False
)
add
(
TestInternational
,
u'президент.рф'
.
encode
(
'idna'
),
'idna'
,
require_equal_errors
=
False
)
@
skipWithoutExternalNetwork
(
"Tries to resolve and compare hostnames/addrinfo"
)
class
TestInterrupted_gethostbyname
(
gevent
.
testing
.
timing
.
AbstractGenericWaitTestCase
):
...
...
src/gevent/tests/test__socket_dns6.py
View file @
a74ba95c
...
...
@@ -61,10 +61,10 @@ class Test6(TestCase):
if
not
OSX
and
RESOLVER_DNSPYTHON
:
# It raises gaierror instead of socket.error,
# which is not great and leads to failures.
def
_run_test_getnameinfo
(
self
,
*
_args
):
def
_run_test_getnameinfo
(
self
,
*
_args
,
**
_kwargs
):
return
(),
0
,
(),
0
def
_run_test_gethostbyname
(
self
,
*
_args
):
def
_run_test_gethostbyname
(
self
,
*
_args
,
**
_kwargs
):
raise
unittest
.
SkipTest
(
"gethostbyname[_ex] does not support IPV6"
)
_run_test_gethostbyname_ex
=
_run_test_gethostbyname
...
...
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