fix secure tab

This commit is contained in:
2026-09-13 08:15:09 +02:00
parent 1e10a7dddb
commit 69ad7a550f
2478 changed files with 602241 additions and 1 deletions
@@ -0,0 +1,81 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQChqfmG6uOG95Jb
7uRi6yxohJ8GOR3gi39yX6JB+Xdukvqxy2/vsjH1+CF1i8jKZZO0hJLGT+/GmKIc
1c0XUEjVoQvCNQHIaDTXiUXOGXfkQNKR0vtJH5ZOZn/tvYAKPniYPmHuF3TpAB6H
ouLpyIC55SXdK7pTEbmU7J1aBjugn3O56cu6FzjU1j/0QVUVGloxApLvv57bmINa
X9ygKsh/ug0lhV1RwYLJ9UX57m95FIlcofa98tCuoKi++G+sWsjopDXVmsiTbjZf
s72kcDUTRYKNZbRFRRETORdOVRHxlAIPEn4QFYn/3wVSNFvfeY0j8RI5YcPLU66B
atun6HU+YAs6z8Qc8S1EMElJdoyVeLCqLA07btICzKq2I16TZAOWVng2P7NOtibA
eCzDAxAxJ3Oby+BVikKcu8WmJLxGvRvaPljdD76xjPB5NK6O0J62C3uU3EWhPODX
9H5l/WF+aNRqSccgs0Umddj33N+b/mTJnHn1GpanThrv1UfOFGKfxjemwESz66d1
iqD7iXvTxt7yZeU7LIMRgDqhVe6zoBpJEeWl9YYyfGPwgIOhwzNVZ5WkzQARs7si
3j3Wkmyca7hEN8qq8DkLWNf1PTcIwo/239wKRbyW3Z+U4IGRrVMdeSoC2JpRAx/e
EXTjuUePQlHCvwW9iiY7jTjDfbIvpwIDAQABAoICAC3CJMTRe3FaZezro210T2+O
Ck0CobhLA9nlw9GUwP9lTtxATwCzmXybrSzOUhknwzUXSUwkmCPIVCqBQbnVmagO
G3vu8QA+rqZLTpzVjJ/o0TFBXKsH681pKdCrELDVmeDN135C2W6SABI4Qq4VeIol
mCAQHn8gxzyl9Kvkk8AVIfZ/fJDBve5Qbm2+iEye1uSEa/68aEST2Kod9B7JvVKZ
4Nq78vwPH+v2JsZlfNvyuiakGWkOb47eHqVfQIyybaebwzkgxKEmUvGnuIfw0rUP
ubI4FVx9/iVIxZYAckHEuQh3HYOD9TmdcK4h79dDWnXP6G6hg3/rwbsT+fR+0aBQ
9rkKnA4uToGikYmplixAQ/jDBwMs3VQqenO+YBIsC4HEZ0fJUbs+l4LEnuUJxYcR
UlAvnVQXa1WGne3Yzb2xONWeiocKfhcdJ2JuQo00UR74+2Qonxn/WpimvlLCBDgI
uKxHCSWOgv5yPpU2kwTPIjORXcy/y2G9K2bnsQCzznPRDyNkZmavQxxG6greFcrO
/0yhRPuBgxKBRvXPO+F5fybKFlU9IPLFehV60jLUybBejab/lMJyxdkh9UMu2Xqy
FVsRGazJt6T6AGp6TFEEcFUQw7qXNhVo9S7zGGaJFJdYc+Vx8QJRoCe8EAYVH7Mp
b/eYGhHaKg6iG7QCjPPxAoIBAQDN54wtuDqpAA+4PmqhiEhQKhabNqAoVmAWUxnJ
Db4Zzvkkc3Fo/Yg0HnQVaT0KmkcxY7397lTdtiwNkWPgJ0f6+g7L4K7PA7xh/q84
IoXFGvYWwVdiVXLR1l06jorpA20clnba6CsbezwcllTq4bWvNnrAcM8l1YrAlRnV
qqqbPL78Rnba4C8q+VFy8r0d9OGnbvFcV7VWJjhr0a3aZbHQ67jPinNiUWvBVFFx
yGrqPMjkeHyiTLMhqQpaSHH67S88rj0g9RKexBaSUrl18QO7xnQHHSCcFWMQOiSN
shNvFri48dnU+Ms6ZLc3MBHbTK6uzP8xJCVnmsz/MWPGkQZFAoIBAQDI/vj/3/y/
EpIawyHN7PQAMoto4AQF6sVasrgGd1tRsJnGKrCugH9gILvyke3L7qg0JTV3bDJY
e8+vH1vC3NV7PsOlCFjMtRWG0lRbCh/b7Qe3pCvPu4mbFhJgMT/mz+vbl5zvcdgX
kvne+St/267NKnY5gHBDhqitBwkZwNlTWJ0zVmTecKXn/KwjS9lX1qU3HiT3UFkd
5Y5Nt5lj1IOK/6NCXkxVkgOc4Zjcxx138Cg03VJhIiHTusRq6z9iTSTDubhkaSbi
2nadptFBiQtkVhAJ5G53U7pl/pIhhiJy901bu/v/wrIMJ2l6hiZIcLrbg6VGXxjV
5dB7LDEtKoL7AoIBAQC8+ffA+mX0N9c1nSuWh5L+6DIJUHBbtTLJKonu6gsAeuJU
3xNGbfK1CwI1qHnaolAW91knlrcTKaBy726ACu1YXmp4GgW2f9JFCk/csGqfxaf4
qIg/+va/ugOku7CoPXnGFB6PuSffOBKqlhrn3DI41kKBHsgwDDYlnHKylMmyYmVS
+oUZS0pfIaXsXvbNaLQ2TG9+9gy7Pabo5e+vE0jI25+p84MEyH+iV3XMfUoLI7Cp
aB/TgZuimBelVvotd8Sz56K4/dSSHJwuvXfz1Dk9/Nz+rnAAcOyTtxlXZwnJGkx9
iZMIkTNMq6UwJJEu+ckVK5ZHjso5tWzSBo1xcCcVAoIBAQCPL0x1A7zK5VDd7cqE
J1w/U8KKiKN1D6VeElkUiiysyjERwdGxzmpvMYKSsDCGCdMbqrInDBXlgPYXnDBD
ZgxSywiW5ZZU5l+advWPEWxWwMmxoitvxfqmV5fpnMwYAmDUQ3KSBTjaumJ03G6H
nBkvoSMtnXjcMe6xrIRoK0Dmpgb+znn3GKqn1BFQ57TCZW+3DytoX33M1X6FkNie
DINVHv3Pxtt8ThNyzCeYh+RPT+9kkZIhDi6o5bENNd8miSw6nnBkX6BLFTRQ5MjH
dfh+luzAD1I+gZAVHsA9T4/09IXQZt+DeNBb5iu3FB/rlRsYS/UOZ6qKnjfhtz6l
HVbHAoIBAFjNY/UPJDxQ/uG+rMU0nrmSBRGdgBvQkcefjWX/LIZV3MjNilUQ+B2a
lXz5AHGmHRnnwQsBVfN8rf4qQLln8l34Kgm7+cIFavgfg2oqVbNyNgezSlUmRq0J
Ttf3xYJtRgRUx8F+BcgJXMqlNGTMQJY8wawM/ATkwkbmSwGOKe04sBeIkwEycMId
BupvfN5lxDrKqJVPSl1t5Rh4us95CNh22/c5Tq5rsynl02ZB4swlcsVTdv8FSGmM
QVf/MkWXGN/x4lHJhKyklHMGv15GGvys1nlPTstMfUYs55ioWRW46TXQ8vOyzzpg
67xzBKYFEde+hgYk7X1Xeqj8A6bsqro=
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIFCzCCAvOgAwIBAgIUePnEKFfhxpt3oypt6nTicAGTFJowDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTIxMDcwODExMzQzNVoYDzIxMjEw
NjE0MTEzNDM1WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQChqfmG6uOG95Jb7uRi6yxohJ8GOR3gi39yX6JB+Xdu
kvqxy2/vsjH1+CF1i8jKZZO0hJLGT+/GmKIc1c0XUEjVoQvCNQHIaDTXiUXOGXfk
QNKR0vtJH5ZOZn/tvYAKPniYPmHuF3TpAB6HouLpyIC55SXdK7pTEbmU7J1aBjug
n3O56cu6FzjU1j/0QVUVGloxApLvv57bmINaX9ygKsh/ug0lhV1RwYLJ9UX57m95
FIlcofa98tCuoKi++G+sWsjopDXVmsiTbjZfs72kcDUTRYKNZbRFRRETORdOVRHx
lAIPEn4QFYn/3wVSNFvfeY0j8RI5YcPLU66Batun6HU+YAs6z8Qc8S1EMElJdoyV
eLCqLA07btICzKq2I16TZAOWVng2P7NOtibAeCzDAxAxJ3Oby+BVikKcu8WmJLxG
vRvaPljdD76xjPB5NK6O0J62C3uU3EWhPODX9H5l/WF+aNRqSccgs0Umddj33N+b
/mTJnHn1GpanThrv1UfOFGKfxjemwESz66d1iqD7iXvTxt7yZeU7LIMRgDqhVe6z
oBpJEeWl9YYyfGPwgIOhwzNVZ5WkzQARs7si3j3Wkmyca7hEN8qq8DkLWNf1PTcI
wo/239wKRbyW3Z+U4IGRrVMdeSoC2JpRAx/eEXTjuUePQlHCvwW9iiY7jTjDfbIv
pwIDAQABo1MwUTAdBgNVHQ4EFgQUTUfShFbaXGMwrWEAkm05sXFH/x4wHwYDVR0j
BBgwFoAUTUfShFbaXGMwrWEAkm05sXFH/x4wDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAe65ORDx0NDxTo1q6EY221KS3vEezUNBdZNaeOQsQeUAY
lEO5iZ+2QLIVlWC5UtvISK96FU2CX0ucgAGfHS2ZB7o8i95fbjG2qrWC+VUH4V/6
jse9jlfGlYGkPuU5onNIDGcZ7gay3n0prCDiguAmCzV419GnGDWgSSgyVNCp/0tx
b7pR5cVr0kZ5bTZjiysEEprkG2ofAlXzj09VGtTfM8gQvCz9Puj7pGzw2iaIEQVk
hSGjoRWlI5x6+o16JOTHXzv9cYRUfDX6tjw3nQJIeMipuUkR8pkHUFjG3EeJEtO3
X/GO0G8rwUPaZiskGPiMZj7XqoVclnYL7JtntwUHR/dU5A/EhDfhgEfTXTqT78Oe
cKri+VJE+G/hYxbP0FNYaDtqIwJcX1tsy4HOpKVBncc+K/PvXElVsyQET/+uwH7p
Wm5ymndnuLoiQrWIA4nJC6rVwR4GPijuN0NCKcVdE+8jlOCBs3VBJTWKuu0J80RP
71iZy03AoK1YY4+nHglmE9HetAgSsbGh2fWC7DUS/4JzLSzOBeb+nn74zfmIfMU+
qUArFXvVGAtjmZZ/63cWzXDMZsp1BZ+O5dx6Gi2QtjgGYhh6DhW7ocQYXDkAeN/O
K1Yzwq/G4AEQA0k0/1I+F0Rdlo41+7tOp+LMCOoZXqUzhM0ZQ2sf3QclubxLX9U=
-----END CERTIFICATE-----
@@ -0,0 +1,6 @@
#!/usr/bin/env python
from __future__ import print_function, absolute_import, division
if __name__ == '__main__':
from gevent.testing import testrunner
testrunner.main()
@@ -0,0 +1,3 @@
from gevent import sleep
sleep(0.01)
x = "done"
@@ -0,0 +1 @@
__import__('_import_patch')
@@ -0,0 +1,2 @@
import gevent.monkey
gevent.monkey.patch_all()
@@ -0,0 +1,26 @@
# test__import_wait.py calls this via an import statement,
# so all of this is happening with import locks held (especially on py2)
import gevent
def fn2():
return 2
# A blocking function doesn't raise LoopExit
def fn():
return gevent.wait([gevent.spawn(fn2), gevent.spawn(fn2)])
gevent.spawn(fn).get()
# Marshalling the traceback across greenlets doesn't
# raise LoopExit
def raise_name_error():
raise NameError("ThisIsExpected")
try:
gevent.spawn(raise_name_error).get()
raise AssertionError("Should fail")
except NameError as e:
x = e
@@ -0,0 +1,2 @@
# We simply import a stdlib module
__import__('netrc')
@@ -0,0 +1,13 @@
import gevent
# For reproducing #728: We spawn a greenlet at import time,
# that itself wants to import, and wait on it at import time.
# If we're the only greenlet running, and locks aren't granular
# enough, this results in a LoopExit (and also a lock deadlock)
def f():
__import__('_imports_at_top_level')
g = gevent.spawn(f)
g.get()
@@ -0,0 +1,36 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
Just bad cert data
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
Just bad cert data
-----END CERTIFICATE-----
@@ -0,0 +1,40 @@
-----BEGIN RSA PRIVATE KEY-----
Bad Key, though the cert should be OK
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
iHkC6gGdBJhogs4=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
Bad Key, though the cert should be OK
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
iHkC6gGdBJhogs4=
-----END CERTIFICATE-----
@@ -0,0 +1,4 @@
import socket
import gevent.socket as gevent_socket
gevent_socket.getaddrinfo(u'gevent.org', None, socket.AF_INET)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
-----BEGIN CERTIFICATE-----
MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
-----END CERTIFICATE-----
@@ -0,0 +1,32 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
iHkC6gGdBJhogs4=
-----END CERTIFICATE-----
@@ -0,0 +1,560 @@
# This is a list of known failures (=bugs).
# The tests listed there must fail (or testrunner.py will report error) unless they are prefixed with FLAKY
# in which cases the result of them is simply ignored
from __future__ import print_function
import sys
import struct
from gevent.testing import sysinfo
class Condition(object):
__slots__ = ()
def __and__(self, other):
return AndCondition(self, other)
def __or__(self, other):
return OrCondition(self, other)
def __bool__(self):
raise NotImplementedError
class AbstractBinaryCondition(Condition): # pylint:disable=abstract-method
__slots__ = (
'lhs',
'rhs',
)
OP = None
def __init__(self, lhs, rhs):
self.lhs = lhs
self.rhs = rhs
def __repr__(self):
return "(%r %s %r)" % (
self.lhs,
self.OP,
self.rhs
)
class OrCondition(AbstractBinaryCondition):
__slots__ = ()
OP = '|'
def __bool__(self):
return bool(self.lhs) or bool(self.rhs)
class AndCondition(AbstractBinaryCondition):
__slots__ = ()
OP = '&'
def __bool__(self):
return bool(self.lhs) and bool(self.rhs)
class ConstantCondition(Condition):
__slots__ = (
'value',
'__name__',
)
def __init__(self, value, name=None):
self.value = bool(value)
self.__name__ = name or str(value)
def __bool__(self):
return self.value
def __repr__(self):
return self.__name__
ALWAYS = ConstantCondition(True)
NEVER = ConstantCondition(False)
class _AttrCondition(ConstantCondition):
__slots__ = (
)
def __init__(self, name):
ConstantCondition.__init__(self, getattr(sysinfo, name), name)
PYPY = _AttrCondition('PYPY')
PYPY3 = _AttrCondition('PYPY3')
PY3 = _AttrCondition('PY3')
OSX = _AttrCondition('OSX')
LIBUV = _AttrCondition('LIBUV')
WIN = _AttrCondition('WIN')
APPVEYOR = _AttrCondition('RUNNING_ON_APPVEYOR')
TRAVIS = _AttrCondition('RUNNING_ON_TRAVIS')
CI = _AttrCondition('RUNNING_ON_CI')
LEAKTEST = _AttrCondition('RUN_LEAKCHECKS')
COVERAGE = _AttrCondition('RUN_COVERAGE')
RESOLVER_NOT_SYSTEM = _AttrCondition('RESOLVER_NOT_SYSTEM')
BIT_64 = ConstantCondition(struct.calcsize('P') * 8 == 64, 'BIT_64')
PY380_EXACTLY = ConstantCondition(sys.version_info[:3] == (3, 8, 0), 'PY380_EXACTLY')
PY312B3_EXACTLY = ConstantCondition(sys.version_info == (3, 12, 0, 'beta', 3))
PY312B4_EXACTLY = ConstantCondition(sys.version_info == (3, 12, 0, 'beta', 4))
PY313LT5 = ConstantCondition(
sys.version_info[:2] == (3, 13)
and sys.version_info[2] < 5
)
class _Definition(object):
__slots__ = (
'__name__',
# When does the class of this condition apply?
'when',
# When should this test be run alone, if it's run?
'run_alone',
# Should this test be ignored during coverage measurement?
'ignore_coverage',
# {name: (Condition, value)}
'options',
)
def __init__(self, when, run_alone, ignore_coverage, options):
assert isinstance(when, Condition)
assert isinstance(run_alone, Condition)
assert isinstance(ignore_coverage, Condition)
self.when = when
self.__name__ = None # pylint:disable=non-str-assignment-to-dunder-name
self.run_alone = run_alone
self.ignore_coverage = ignore_coverage
if options:
for v in options.values():
assert isinstance(v, tuple) and len(v) == 2
assert isinstance(v[0], Condition)
self.options = options
def __set_name__(self, owner, name):
self.__name__ = name
def __repr__(self):
return '<%s for %s when=%r=%s run_alone=%r=%s>' % (
type(self).__name__,
self.__name__,
self.when, bool(self.when),
self.run_alone, bool(self.run_alone)
)
class _Action(_Definition):
__slots__ = (
'reason',
)
def __init__(self, reason='', when=ALWAYS, run_alone=NEVER, ignore_coverage=NEVER,
options=None):
_Definition.__init__(self, when, run_alone, ignore_coverage, options)
self.reason = reason
class RunAlone(_Action):
__slots__ = ()
def __init__(self, reason='', when=ALWAYS, ignore_coverage=NEVER):
_Action.__init__(self, reason, run_alone=when, ignore_coverage=ignore_coverage)
class Failing(_Action):
__slots__ = ()
class Flaky(Failing):
__slots__ = ()
class Ignored(_Action):
__slots__ = ()
class Multi(object):
def __init__(self):
self._conds = []
def flaky(self, reason='', when=True, ignore_coverage=NEVER, run_alone=NEVER):
self._conds.append(
Flaky(
reason, when=when,
ignore_coverage=ignore_coverage,
run_alone=run_alone,
)
)
return self
def ignored(self, reason='', when=True):
self._conds.append(Ignored(reason, when=when))
return self
def __set_name__(self, owner, name):
for c in self._conds:
c.__set_name__(owner, name)
class DefinitionsMeta(type):
# a metaclass on Python 3 that makes sure we only set attributes once. pylint doesn't
# warn about that.
@classmethod
def __prepare__(mcs, name, bases): # pylint:disable=unused-argument,bad-dunder-name
return SetOnceMapping()
class SetOnceMapping(dict):
def __setitem__(self, name, value):
if name in self:
raise AttributeError(name)
dict.__setitem__(self, name, value)
som = SetOnceMapping()
som[1] = 1
try:
som[1] = 2
except AttributeError:
del som
else:
raise AssertionError("SetOnceMapping is broken")
class Definitions(metaclass=DefinitionsMeta):
test__util = RunAlone(
"""
If we have extra greenlets hanging around due to changes in GC, we won't
match the expected output.
This arises on CI 3.14.3 when combined with test__threadpool;
it was previously seen with some specific 3.12 releases.
For safety, always run alone.
""",
when=(CI)
)
test__issue6 = Flaky(
"""test__issue6 (see comments in test file) is really flaky on both Travis and Appveyor;
on Travis we could just run the test again (but that gets old fast), but on appveyor
we don't have that option without a new commit---and sometimes we really need a build
to succeed in order to get a release wheel"""
)
test__core_fork = Ignored(
"""fork watchers don't get called on windows
because fork is not a concept windows has.
See this file for a detailed explanation.""",
when=WIN
)
test__greenletset = Flaky(
when=WIN,
ignore_coverage=PYPY
)
test__example_udp_client = test__example_udp_server = Flaky(
"""
These both run on port 9000 and can step on each other...seems
like the appveyor containers aren't fully port safe? Or it
takes longer for the processes to shut down? Or we run them in
a different order in the process pool than we do other places?
On PyPy on Travis, this fails to get the correct results,
sometimes. I can't reproduce locally
""",
when=APPVEYOR | (PYPY & TRAVIS)
)
# This one sometimes randomly closes connections, but no indication
# of a server crash, only a client side close.
test__server_pywsgi = Flaky(when=APPVEYOR)
test_threading = Multi().ignored(
"""
This one seems to just stop right after patching is done. It
passes on a local win 10 vm, and the main test_threading_2.py
does as well. Based on the printouts we added, it appears to
not even finish importing:
https://ci.appveyor.com/project/denik/gevent/build/1.0.1277/job/tpvhesij5gldjxqw#L1190
Ignored because it takes two minutes to time out.
""",
when=APPVEYOR & LIBUV & PYPY
).flaky(
"""
test_set_and_clear in Py3 relies on 5 threads all starting and
coming to an Event wait point while a sixth thread sleeps for a half
second. The sixth thread then does something and checks that
the 5 threads were all at the wait point. But the timing is sometimes
too tight for appveyor. This happens even if Event isn't
monkey-patched
""",
when=APPVEYOR & PY3
)
test_ftplib = Flaky(
r"""
could be a problem of appveyor - not sure
======================================================================
ERROR: test_af (__main__.TestIPv6Environment)
----------------------------------------------------------------------
File "C:\Python27-x64\lib\ftplib.py", line 135, in connect
self.sock = socket.create_connection((self.host, self.port), self.timeout)
File "c:\projects\gevent\gevent\socket.py", line 73, in create_connection
raise err
error: [Errno 10049] [Error 10049] The requested address is not valid in its context.
XXX: On Jan 3 2016 this suddenly started passing on Py27/64; no idea why, the python version
was 2.7.11 before and after.
""",
when=APPVEYOR & BIT_64
)
test__backdoor = Flaky(when=LEAKTEST | PYPY)
test__socket_errors = Flaky(when=LEAKTEST)
test_signal = Multi().flaky(
"On Travis, this very frequently fails due to timing",
when=TRAVIS & LEAKTEST,
# Partial workaround for the _testcapi issue on PyPy,
# but also because signal delivery can sometimes be slow, and this
# spawn processes of its own
run_alone=APPVEYOR,
).ignored(
"""
This fails to run a single test. It looks like just importing the module
can hang. All I see is the output from patch_all()
""",
when=APPVEYOR & PYPY3
)
test__monkey_sigchld_2 = Ignored(
"""
This hangs for no apparent reason when run by the testrunner,
even wher maked standalone when run standalone from the
command line, it's fine. Issue in pypy2 6.0?
""",
when=PYPY & LIBUV
)
test_ssl = Ignored(
"""
PyPy 7.0 and 7.1 on Travis with Ubunto Xenial 16.04 can't
allocate SSL Context objects, either in Python 2.7 or 3.6.
There must be some library incompatibility. No point even
running them. XXX: Remember to turn this back on.
On Windows, with PyPy3.7 7.3.7, there seem to be all kind of certificate
errors.
""",
when=(PYPY & TRAVIS) | (PYPY3 & WIN)
)
test_httpservers = Ignored(
"""
All the CGI tests hang. There appear to be subprocess problems.
""",
when=PYPY3 & WIN
)
test__pywsgi = Ignored(
"""
XXX: Re-enable this when we can investigate more. This has
started crashing with a SystemError. I cannot reproduce with
the same version on macOS and I cannot reproduce with the same
version in a Linux vm. Commenting out individual tests just
moves the crash around.
https://bitbucket.org/pypy/pypy/issues/2769/systemerror-unexpected-internal-exception
On Appveyor 3.8.0, for some reason this takes *way* too long, about 100s, which
often goes just over the default timeout of 100s. This makes no sense.
But it also takes nearly that long in 3.7. 3.6 and earlier are much faster.
It also takes just over 100s on PyPy 3.7.
""",
when=(PYPY & TRAVIS & LIBUV) | PY380_EXACTLY,
# https://bitbucket.org/pypy/pypy/issues/2769/systemerror-unexpected-internal-exception
run_alone=(CI & LEAKTEST & PY3) | (PYPY & LIBUV),
# This often takes much longer on PyPy on CI.
options={'timeout': (CI & PYPY, 180)},
)
test_subprocess = Multi().flaky(
"Unknown, can't reproduce locally; times out one test",
when=PYPY & PY3 & TRAVIS,
ignore_coverage=ALWAYS,
).ignored(
"Tests don't even start before the process times out.",
when=PYPY3 & WIN
)
test__threadpool = Ignored(
"""
XXX: Re-enable these when we have more time to investigate.
This test, which normally takes ~60s, sometimes
hangs forever after running several tests. I cannot reproduce,
it seems highly load dependent. Observed with both libev and libuv.
""",
when=TRAVIS & (PYPY | OSX),
# This often takes much longer on PyPy on CI.
options={'timeout': (CI & PYPY, 180)},
)
test__threading_2 = Ignored(
"""
This test, which normally takes 4-5s, sometimes
hangs forever after running two tests. I cannot reproduce,
it seems highly load dependent. Observed with both libev and libuv.
""",
when=TRAVIS & (PYPY | OSX),
# This often takes much longer on PyPy on CI.
options={'timeout': (CI & PYPY, 180)},
)
test__issue230 = Ignored(
"""
This rarely hangs for unknown reasons. I cannot reproduce
locally.
""",
when=TRAVIS & OSX
)
test_selectors = Flaky(
"""
Timing issues on appveyor.
""",
when=PY3 & APPVEYOR,
ignore_coverage=ALWAYS,
)
test__example_portforwarder = Flaky(
"""
This one sometimes times out, often after output "The process
with PID XXX could not be terminated. Reason: There is no
running instance of the task.",
""",
when=APPVEYOR | COVERAGE
)
test__issue302monkey = test__threading_vs_settrace = Flaky(
"""
The gevent concurrency plugin tends to slow things
down and get us past our default timeout value. These
tests in particular are sensitive to it. So in fact we just turn them
off.
""",
when=COVERAGE,
ignore_coverage=ALWAYS,
)
test__hub_join_timeout = Ignored(
r"""
This sometimes times out. It appears to happen when the
times take too long and a test raises a FlakyTestTimeout error,
aka a unittest.SkipTest error. This probably indicates that we're
not cleaning something up correctly:
.....ss
GEVENTTEST_USE_RESOURCES=-network C:\Python38-x64\python.exe -u \
-mgevent.tests.test__hub_join_timeout [code TIMEOUT] [took 100.4s]
""",
when=APPVEYOR
)
test__example_wsgiserver = test__example_webproxy = RunAlone(
"""
These share the same port, which means they can conflict
between concurrent test runs too
XXX: Fix this by dynamically picking a port.
""",
)
test__pool = RunAlone(
"""
On a heavily loaded box, these can all take upwards of 200s.
""",
when=(CI & LEAKTEST) | (PYPY3 & APPVEYOR)
)
test_socket = RunAlone(
"Sometimes has unexpected timeouts",
when=CI & PYPY & PY3,
ignore_coverage=ALWAYS, # times out
)
test__refcount = Ignored(
"Sometimes fails to connect for no reason",
when=(CI & OSX) | (CI & PYPY) | APPVEYOR,
ignore_coverage=PYPY
)
test__doctests = Ignored(
"Sometimes times out during/after gevent._config.Config",
when=CI & OSX
)
test_httplib = Ignored(
"""
Imports ``test.support.testcase.ExtraAssertions``
which doesn't exist yet.
""",
when=PY313LT5,
)
# tests that can't be run when coverage is enabled
# TODO: Now that we have this declarative, we could eliminate this list,
# just add them to the main IGNORED_TESTS list.
IGNORE_COVERAGE = [
]
# A mapping from test file basename to a dictionary of
# options that will be applied on top of the DEFAULT_RUN_OPTIONS.
TEST_FILE_OPTIONS = {
}
FAILING_TESTS = []
IGNORED_TESTS = []
# tests that don't do well when run on busy box
# or that are mutually exclusive
RUN_ALONE = [
]
def populate(): # pylint:disable=too-many-branches
# TODO: Maybe move to the metaclass.
# TODO: This could be better.
for k, v in Definitions.__dict__.items():
if isinstance(v, Multi):
actions = v._conds
else:
actions = (v,)
test_name = k + '.py'
del k, v
for action in actions:
if not isinstance(action, _Action):
continue
if action.run_alone:
RUN_ALONE.append(test_name)
if action.ignore_coverage:
IGNORE_COVERAGE.append(test_name)
if action.options:
for opt_name, (condition, value) in action.options.items():
# TODO: Verify that this doesn't match more than once.
if condition:
TEST_FILE_OPTIONS.setdefault(test_name, {})[opt_name] = value
if action.when:
if isinstance(action, Ignored):
IGNORED_TESTS.append(test_name)
elif isinstance(action, Flaky):
FAILING_TESTS.append('FLAKY ' + test_name)
elif isinstance(action, Failing):
FAILING_TESTS.append(test_name)
FAILING_TESTS.sort()
IGNORED_TESTS.sort()
RUN_ALONE.sort()
populate()
if __name__ == '__main__':
print('known_failures:\n', FAILING_TESTS)
print('ignored tests:\n', IGNORED_TESTS)
print('run alone:\n', RUN_ALONE)
print('options:\n', TEST_FILE_OPTIONS)
print("ignore during coverage:\n", IGNORE_COVERAGE)
@@ -0,0 +1,764 @@
"""
Various tests for synchronization primitives.
"""
# pylint:disable=no-member,abstract-method
import sys
import time
try:
from thread import start_new_thread, get_ident
except ImportError:
from _thread import start_new_thread, get_ident
import threading
import unittest
from gevent.testing import support
from gevent.testing.testcase import TimeAssertMixin
def _wait():
# A crude wait/yield function not relying on synchronization primitives.
time.sleep(0.01)
class Bunch(object):
"""
A bunch of threads.
"""
def __init__(self, f, n, wait_before_exit=False):
"""
Construct a bunch of `n` threads running the same function `f`.
If `wait_before_exit` is True, the threads won't terminate until
do_finish() is called.
"""
self.f = f
self.n = n
self.started = []
self.finished = []
self._can_exit = not wait_before_exit
def task():
tid = get_ident()
self.started.append(tid)
try:
f()
finally:
self.finished.append(tid)
while not self._can_exit:
_wait()
for _ in range(n):
start_new_thread(task, ())
def wait_for_started(self):
while len(self.started) < self.n:
_wait()
def wait_for_finished(self):
while len(self.finished) < self.n:
_wait()
def do_finish(self):
self._can_exit = True
class BaseTestCase(TimeAssertMixin, unittest.TestCase):
def setUp(self):
self._threads = support.threading_setup()
def tearDown(self):
support.threading_cleanup(*self._threads)
support.reap_children()
class BaseLockTests(BaseTestCase):
"""
Tests for both recursive and non-recursive locks.
"""
def locktype(self):
raise NotImplementedError()
def test_constructor(self):
lock = self.locktype()
del lock
def test_acquire_destroy(self):
lock = self.locktype()
lock.acquire()
del lock
def test_acquire_release(self):
lock = self.locktype()
lock.acquire()
lock.release()
del lock
def test_try_acquire(self):
lock = self.locktype()
self.assertTrue(lock.acquire(False))
lock.release()
def test_try_acquire_contended(self):
lock = self.locktype()
lock.acquire()
result = []
def f():
result.append(lock.acquire(False))
Bunch(f, 1).wait_for_finished()
self.assertFalse(result[0])
lock.release()
def test_acquire_contended(self):
lock = self.locktype()
lock.acquire()
N = 5
def f():
lock.acquire()
lock.release()
b = Bunch(f, N)
b.wait_for_started()
_wait()
self.assertEqual(len(b.finished), 0)
lock.release()
b.wait_for_finished()
self.assertEqual(len(b.finished), N)
def test_with(self):
lock = self.locktype()
def f():
lock.acquire()
lock.release()
def _with(err=None):
with lock:
if err is not None:
raise err # pylint:disable=raising-bad-type
_with()
# Check the lock is unacquired
Bunch(f, 1).wait_for_finished()
self.assertRaises(TypeError, _with, TypeError)
# Check the lock is unacquired
Bunch(f, 1).wait_for_finished()
def test_thread_leak(self):
# The lock shouldn't leak a Thread instance when used from a foreign
# (non-threading) thread.
lock = self.locktype()
def f():
lock.acquire()
lock.release()
n = len(threading.enumerate())
# We run many threads in the hope that existing threads ids won't
# be recycled.
Bunch(f, 15).wait_for_finished()
self.assertEqual(n, len(threading.enumerate()))
class LockTests(BaseLockTests): # pylint:disable=abstract-method
"""
Tests for non-recursive, weak locks
(which can be acquired and released from different threads).
"""
def test_reacquire(self):
# Lock needs to be released before re-acquiring.
lock = self.locktype()
phase = []
def f():
lock.acquire()
phase.append(None)
lock.acquire()
phase.append(None)
start_new_thread(f, ())
while not phase:
_wait()
_wait()
self.assertEqual(len(phase), 1)
lock.release()
while len(phase) == 1:
_wait()
self.assertEqual(len(phase), 2)
def test_different_thread(self):
# Lock can be released from a different thread.
lock = self.locktype()
lock.acquire()
def f():
lock.release()
b = Bunch(f, 1)
b.wait_for_finished()
lock.acquire()
lock.release()
class RLockTests(BaseLockTests):
"""
Tests for recursive locks.
"""
def test_reacquire(self):
lock = self.locktype()
lock.acquire()
lock.acquire()
lock.release()
lock.acquire()
lock.release()
lock.release()
def test_release_unacquired(self):
# Cannot release an unacquired lock
lock = self.locktype()
self.assertRaises(RuntimeError, lock.release)
lock.acquire()
lock.acquire()
lock.release()
lock.acquire()
lock.release()
lock.release()
self.assertRaises(RuntimeError, lock.release)
def test_different_thread(self):
# Cannot release from a different thread
lock = self.locktype()
def f():
lock.acquire()
b = Bunch(f, 1, True)
try:
self.assertRaises(RuntimeError, lock.release)
finally:
b.do_finish()
def test__is_owned(self):
lock = self.locktype()
self.assertFalse(lock._is_owned())
lock.acquire()
self.assertTrue(lock._is_owned())
lock.acquire()
self.assertTrue(lock._is_owned())
result = []
def f():
result.append(lock._is_owned())
Bunch(f, 1).wait_for_finished()
self.assertFalse(result[0])
lock.release()
self.assertTrue(lock._is_owned())
lock.release()
self.assertFalse(lock._is_owned())
class EventTests(BaseTestCase):
"""
Tests for Event objects.
"""
def eventtype(self):
raise NotImplementedError()
def test_is_set(self):
evt = self.eventtype()
self.assertFalse(evt.is_set())
evt.set()
self.assertTrue(evt.is_set())
evt.set()
self.assertTrue(evt.is_set())
evt.clear()
self.assertFalse(evt.is_set())
evt.clear()
self.assertFalse(evt.is_set())
def _check_notify(self, evt):
# All threads get notified
N = 5
results1 = []
results2 = []
def f():
evt.wait()
results1.append(evt.is_set())
evt.wait()
results2.append(evt.is_set())
b = Bunch(f, N)
b.wait_for_started()
_wait()
self.assertEqual(len(results1), 0)
evt.set()
b.wait_for_finished()
self.assertEqual(results1, [True] * N)
self.assertEqual(results2, [True] * N)
def test_notify(self):
evt = self.eventtype()
self._check_notify(evt)
# Another time, after an explicit clear()
evt.set()
evt.clear()
self._check_notify(evt)
def test_timeout(self):
evt = self.eventtype()
results1 = []
results2 = []
N = 5
def f():
evt.wait(0.0)
results1.append(evt.is_set())
t1 = time.time()
evt.wait(0.2)
r = evt.is_set()
t2 = time.time()
results2.append((r, t2 - t1))
Bunch(f, N).wait_for_finished()
self.assertEqual(results1, [False] * N)
for r, dt in results2:
self.assertFalse(r)
self.assertTimeWithinRange(dt, 0.18, 10)
# The event is set
results1 = []
results2 = []
evt.set()
Bunch(f, N).wait_for_finished()
self.assertEqual(results1, [True] * N)
for r, dt in results2:
self.assertTrue(r)
class ConditionTests(BaseTestCase):
"""
Tests for condition variables.
"""
def condtype(self, *args):
raise NotImplementedError()
def test_acquire(self):
cond = self.condtype()
# Be default we have an RLock: the condition can be acquired multiple
# times.
# pylint:disable=consider-using-with
cond.acquire()
cond.acquire()
cond.release()
cond.release()
lock = threading.Lock()
cond = self.condtype(lock)
cond.acquire()
self.assertFalse(lock.acquire(False))
cond.release()
self.assertTrue(lock.acquire(False))
self.assertFalse(cond.acquire(False))
lock.release()
with cond:
self.assertFalse(lock.acquire(False))
def test_unacquired_wait(self):
cond = self.condtype()
self.assertRaises(RuntimeError, cond.wait)
def test_unacquired_notify(self):
cond = self.condtype()
self.assertRaises(RuntimeError, cond.notify)
def _check_notify(self, cond):
N = 5
results1 = []
results2 = []
phase_num = 0
def f():
cond.acquire()
cond.wait()
cond.release()
results1.append(phase_num)
cond.acquire()
cond.wait()
cond.release()
results2.append(phase_num)
b = Bunch(f, N)
b.wait_for_started()
_wait()
self.assertEqual(results1, [])
# Notify 3 threads at first
cond.acquire()
cond.notify(3)
_wait()
phase_num = 1
cond.release()
while len(results1) < 3:
_wait()
self.assertEqual(results1, [1] * 3)
self.assertEqual(results2, [])
# Notify 5 threads: they might be in their first or second wait
cond.acquire()
cond.notify(5)
_wait()
phase_num = 2
cond.release()
while len(results1) + len(results2) < 8:
_wait()
self.assertEqual(results1, [1] * 3 + [2] * 2)
self.assertEqual(results2, [2] * 3)
# Notify all threads: they are all in their second wait
cond.acquire()
cond.notify_all()
_wait()
phase_num = 3
cond.release()
while len(results2) < 5:
_wait()
self.assertEqual(results1, [1] * 3 + [2] * 2)
self.assertEqual(results2, [2] * 3 + [3] * 2)
b.wait_for_finished()
def test_notify(self):
cond = self.condtype()
self._check_notify(cond)
# A second time, to check internal state is still ok.
self._check_notify(cond)
def test_timeout(self):
cond = self.condtype()
results = []
N = 5
def f():
cond.acquire()
t1 = time.time()
cond.wait(0.2)
t2 = time.time()
cond.release()
results.append(t2 - t1)
Bunch(f, N).wait_for_finished()
self.assertEqual(len(results), 5)
for dt in results:
# XXX: libuv sometimes produces 0.19958
self.assertTimeWithinRange(dt, 0.19, 2.0)
class BaseSemaphoreTests(BaseTestCase):
"""
Common tests for {bounded, unbounded} semaphore objects.
"""
def semtype(self, *args):
raise NotImplementedError()
def test_constructor(self):
self.assertRaises(ValueError, self.semtype, value=-1)
# Py3 doesn't have sys.maxint
self.assertRaises((ValueError, OverflowError), self.semtype,
value=-getattr(sys, 'maxint', getattr(sys, 'maxsize', None)))
def test_acquire(self):
sem = self.semtype(1)
sem.acquire()
sem.release()
sem = self.semtype(2)
sem.acquire()
sem.acquire()
sem.release()
sem.release()
def test_acquire_destroy(self):
sem = self.semtype()
sem.acquire()
del sem
def test_acquire_contended(self):
sem = self.semtype(7)
sem.acquire()
#N = 10
results1 = []
results2 = []
phase_num = 0
def f():
sem.acquire()
results1.append(phase_num)
sem.acquire()
results2.append(phase_num)
b = Bunch(f, 10)
b.wait_for_started()
while len(results1) + len(results2) < 6:
_wait()
self.assertEqual(results1 + results2, [0] * 6)
phase_num = 1
for _ in range(7):
sem.release()
while len(results1) + len(results2) < 13:
_wait()
self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7)
phase_num = 2
for _ in range(6):
sem.release()
while len(results1) + len(results2) < 19:
_wait()
self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7 + [2] * 6)
# The semaphore is still locked
self.assertFalse(sem.acquire(False))
# Final release, to let the last thread finish
sem.release()
b.wait_for_finished()
def test_try_acquire(self):
sem = self.semtype(2)
self.assertTrue(sem.acquire(False))
self.assertTrue(sem.acquire(False))
self.assertFalse(sem.acquire(False))
sem.release()
self.assertTrue(sem.acquire(False))
def test_try_acquire_contended(self):
sem = self.semtype(4)
sem.acquire()
results = []
def f():
results.append(sem.acquire(False))
results.append(sem.acquire(False))
Bunch(f, 5).wait_for_finished()
# There can be a thread switch between acquiring the semaphore and
# appending the result, therefore results will not necessarily be
# ordered.
self.assertEqual(sorted(results), [False] * 7 + [True] * 3)
def test_default_value(self):
# The default initial value is 1.
sem = self.semtype()
sem.acquire()
def f():
sem.acquire()
sem.release()
b = Bunch(f, 1)
b.wait_for_started()
_wait()
self.assertFalse(b.finished)
sem.release()
b.wait_for_finished()
def test_with(self):
sem = self.semtype(2)
def _with(err=None):
with sem:
self.assertTrue(sem.acquire(False))
sem.release()
with sem:
self.assertFalse(sem.acquire(False))
if err:
raise err # pylint:disable=raising-bad-type
_with()
self.assertTrue(sem.acquire(False))
sem.release()
self.assertRaises(TypeError, _with, TypeError)
self.assertTrue(sem.acquire(False))
sem.release()
class SemaphoreTests(BaseSemaphoreTests):
"""
Tests for unbounded semaphores.
"""
def test_release_unacquired(self):
# Unbounded releases are allowed and increment the semaphore's value
sem = self.semtype(1)
sem.release()
sem.acquire()
sem.acquire()
sem.release()
class BoundedSemaphoreTests(BaseSemaphoreTests):
"""
Tests for bounded semaphores.
"""
def test_release_unacquired(self):
# Cannot go past the initial value
sem = self.semtype()
self.assertRaises(ValueError, sem.release)
sem.acquire()
sem.release()
self.assertRaises(ValueError, sem.release)
class BarrierTests(BaseTestCase):
"""
Tests for Barrier objects.
"""
N = 5
defaultTimeout = 2.0
def setUp(self):
self.barrier = self.barriertype(self.N, timeout=self.defaultTimeout)
def tearDown(self):
self.barrier.abort()
def run_threads(self, f):
b = Bunch(f, self.N-1)
f()
b.wait_for_finished()
def multipass(self, results, n):
m = self.barrier.parties
self.assertEqual(m, self.N)
for i in range(n):
results[0].append(True)
self.assertEqual(len(results[1]), i * m)
self.barrier.wait()
results[1].append(True)
self.assertEqual(len(results[0]), (i + 1) * m)
self.barrier.wait()
self.assertEqual(self.barrier.n_waiting, 0)
self.assertFalse(self.barrier.broken)
def test_barrier(self, passes=1):
"""
Test that a barrier is passed in lockstep
"""
results = [[], []]
def f():
self.multipass(results, passes)
self.run_threads(f)
def test_barrier_10(self):
"""
Test that a barrier works for 10 consecutive runs
"""
return self.test_barrier(10)
def test_wait_return(self):
"""
test the return value from barrier.wait
"""
results = []
def f():
r = self.barrier.wait()
results.append(r)
self.run_threads(f)
self.assertEqual(sum(results), sum(range(self.N)))
def test_action(self):
"""
Test the 'action' callback
"""
results = []
def action():
results.append(True)
barrier = self.barriertype(self.N, action)
def f():
barrier.wait()
self.assertEqual(len(results), 1)
self.run_threads(f)
def test_abort(self):
"""
Test that an abort will put the barrier in a broken state
"""
results1 = []
results2 = []
def f():
try:
i = self.barrier.wait()
if i == self.N//2:
raise RuntimeError
self.barrier.wait()
results1.append(True)
except threading.BrokenBarrierError:
results2.append(True)
except RuntimeError:
self.barrier.abort()
self.run_threads(f)
self.assertEqual(len(results1), 0)
self.assertEqual(len(results2), self.N-1)
self.assertTrue(self.barrier.broken)
def test_reset(self):
"""
Test that a 'reset' on a barrier frees the waiting threads
"""
results1 = []
results2 = []
results3 = []
def f():
i = self.barrier.wait()
if i == self.N//2:
# Wait until the other threads are all in the barrier.
while self.barrier.n_waiting < self.N-1:
time.sleep(0.001)
self.barrier.reset()
else:
try:
self.barrier.wait()
results1.append(True)
except threading.BrokenBarrierError:
results2.append(True)
# Now, pass the barrier again
self.barrier.wait()
results3.append(True)
self.run_threads(f)
self.assertEqual(len(results1), 0)
self.assertEqual(len(results2), self.N-1)
self.assertEqual(len(results3), self.N)
def test_abort_and_reset(self):
"""
Test that a barrier can be reset after being broken.
"""
results1 = []
results2 = []
results3 = []
barrier2 = self.barriertype(self.N)
def f():
try:
i = self.barrier.wait()
if i == self.N//2:
raise RuntimeError
self.barrier.wait()
results1.append(True)
except threading.BrokenBarrierError:
results2.append(True)
except RuntimeError:
self.barrier.abort()
# Synchronize and reset the barrier. Must synchronize first so
# that everyone has left it when we reset, and after so that no
# one enters it before the reset.
if barrier2.wait() == self.N//2:
self.barrier.reset()
barrier2.wait()
self.barrier.wait()
results3.append(True)
self.run_threads(f)
self.assertEqual(len(results1), 0)
self.assertEqual(len(results2), self.N-1)
self.assertEqual(len(results3), self.N)
def test_timeout(self):
"""
Test wait(timeout)
"""
def f():
i = self.barrier.wait()
if i == self.N // 2:
# One thread is late!
time.sleep(1.0)
# Default timeout is 2.0, so this is shorter.
self.assertRaises(threading.BrokenBarrierError,
self.barrier.wait, 0.5)
self.run_threads(f)
def test_default_timeout(self):
"""
Test the barrier's default timeout
"""
# create a barrier with a low default timeout
barrier = self.barriertype(self.N, timeout=0.3)
def f():
i = barrier.wait()
if i == self.N // 2:
# One thread is later than the default timeout of 0.3s.
time.sleep(1.0)
self.assertRaises(threading.BrokenBarrierError, barrier.wait)
self.run_threads(f)
def test_single_thread(self):
b = self.barriertype(1)
b.wait()
b.wait()
if __name__ == '__main__':
print("This module contains no tests; it is used by other test cases like test_threading_2")
@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
"""
Make a package.
This file has no other functionality. Individual modules in this package
are used for testing, often being run with 'python -m ...' in individual
test cases (functions).
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
@@ -0,0 +1,9 @@
from __future__ import print_function
# This file makes this directory into a runnable package.
# it exists to test 'python -m gevent.monkey monkey_package'
# Note that the __file__ may differ slightly; starting with
# Python 3.9, directly running it gets an abspath, but
# using ``runpy`` doesn't.
import os.path
print(os.path.abspath(__file__))
print(__name__)
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
"""
Test for issue #1526:
- dnspython is imported first;
- no monkey-patching is done.
"""
from __future__ import print_function
from __future__ import absolute_import
import dns # pylint:disable=import-error
assert dns
import gevent.socket as socket # pylint:disable=consider-using-from-import
socket.getfqdn() # create the resolver
from gevent.resolver.dnspython import dns as gdns
import dns.rdtypes # pylint:disable=import-error
assert dns is not gdns, (dns, gdns)
assert dns.rdtypes is not gdns.rdtypes
import sys
print(sorted(sys.modules))
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
Test for issue #1526:
- dnspython is imported first;
- monkey-patching happens early
"""
from __future__ import print_function, absolute_import
from gevent import monkey
monkey.patch_all()
# pylint:disable=import-error
import dns
assert dns
import socket
import sys
socket.getfqdn()
import gevent.resolver.dnspython
from gevent.resolver.dnspython import dns as gdns
from dns import rdtypes # NOT import dns.rdtypes
assert gevent.resolver.dnspython.dns is gdns
assert gdns is not dns, (gdns, dns, "id dns", id(dns))
assert gdns.rdtypes is not rdtypes, (gdns.rdtypes, rdtypes)
assert hasattr(dns, 'rdtypes')
print(sorted(sys.modules))
@@ -0,0 +1,26 @@
from __future__ import print_function
import socket
import sys
import os.path
if sys.argv[1] == 'patched':
print('gevent' in repr(socket.socket))
else:
assert sys.argv[1] == 'stdlib'
print('gevent' not in repr(socket.socket))
print(os.path.abspath(__file__))
if sys.argv[1] == 'patched':
# __package__ is handled differently, for some reason, and
# runpy doesn't let us override it. When we call it, it
# becomes ''. This appears to be against the documentation for
# runpy, which says specifically "If the supplied path
# directly references a script file (whether as source or as
# precompiled byte code), then __file__ will be set to the
# supplied path, and __spec__, __cached__, __loader__ and
# __package__ will all be set to None."
print(__package__ == '') # pylint:disable=compare-to-empty-string
else:
# but the interpreter sets it to None
print(__package__ is None)
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
"""
Test script file, to be used directly as a file.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
# We need some global imports
from textwrap import dedent
def use_import():
return dedent(" text")
if __name__ == '__main__':
import os.path
print(os.path.abspath(__file__))
print(__name__)
print(use_import())
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
"""
This file runs ``gevent.monkey.patch_all()``.
It is intended to be used by ``python -m gevent.monkey <this file>``
to prove that monkey-patching twice doesn't have unfortunate sife effects (such as
breaking the threadpool).
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import sys
from gevent import monkey
from gevent import get_hub
monkey.patch_all(thread=False, sys=True)
def thread_is_greenlet():
from gevent.thread import get_ident as gr_ident
std_thread_mod = 'thread' if bytes is str else '_thread'
thr_ident = monkey.get_original(std_thread_mod, 'get_ident')
return thr_ident() == gr_ident()
is_greenlet = get_hub().threadpool.apply(thread_is_greenlet)
print(is_greenlet)
print(len(sys._current_frames()))
sys.stdout.flush()
sys.stderr.flush()
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
"""
This file *does not* run ``gevent.monkey.patch_all()``.
It is intended to be used by ``python -m gevent.monkey <this file>``
to prove that the threadpool and getting the original value of things
works.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import sys
from gevent import monkey
from gevent import get_hub
from gevent.thread import get_ident as gr_ident
std_thread_mod = 'thread' if bytes is str else '_thread'
thr_ident = monkey.get_original(std_thread_mod, 'get_ident')
print(thr_ident is gr_ident)
def thread_is_greenlet():
return thr_ident() == gr_ident()
is_greenlet = get_hub().threadpool.apply(thread_is_greenlet)
print(is_greenlet)
print(len(sys._current_frames()))
@@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIFCzCCAvOgAwIBAgIUePnEKFfhxpt3oypt6nTicAGTFJowDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTIxMDcwODExMzQzNVoYDzIxMjEw
NjE0MTEzNDM1WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQChqfmG6uOG95Jb7uRi6yxohJ8GOR3gi39yX6JB+Xdu
kvqxy2/vsjH1+CF1i8jKZZO0hJLGT+/GmKIc1c0XUEjVoQvCNQHIaDTXiUXOGXfk
QNKR0vtJH5ZOZn/tvYAKPniYPmHuF3TpAB6HouLpyIC55SXdK7pTEbmU7J1aBjug
n3O56cu6FzjU1j/0QVUVGloxApLvv57bmINaX9ygKsh/ug0lhV1RwYLJ9UX57m95
FIlcofa98tCuoKi++G+sWsjopDXVmsiTbjZfs72kcDUTRYKNZbRFRRETORdOVRHx
lAIPEn4QFYn/3wVSNFvfeY0j8RI5YcPLU66Batun6HU+YAs6z8Qc8S1EMElJdoyV
eLCqLA07btICzKq2I16TZAOWVng2P7NOtibAeCzDAxAxJ3Oby+BVikKcu8WmJLxG
vRvaPljdD76xjPB5NK6O0J62C3uU3EWhPODX9H5l/WF+aNRqSccgs0Umddj33N+b
/mTJnHn1GpanThrv1UfOFGKfxjemwESz66d1iqD7iXvTxt7yZeU7LIMRgDqhVe6z
oBpJEeWl9YYyfGPwgIOhwzNVZ5WkzQARs7si3j3Wkmyca7hEN8qq8DkLWNf1PTcI
wo/239wKRbyW3Z+U4IGRrVMdeSoC2JpRAx/eEXTjuUePQlHCvwW9iiY7jTjDfbIv
pwIDAQABo1MwUTAdBgNVHQ4EFgQUTUfShFbaXGMwrWEAkm05sXFH/x4wHwYDVR0j
BBgwFoAUTUfShFbaXGMwrWEAkm05sXFH/x4wDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAe65ORDx0NDxTo1q6EY221KS3vEezUNBdZNaeOQsQeUAY
lEO5iZ+2QLIVlWC5UtvISK96FU2CX0ucgAGfHS2ZB7o8i95fbjG2qrWC+VUH4V/6
jse9jlfGlYGkPuU5onNIDGcZ7gay3n0prCDiguAmCzV419GnGDWgSSgyVNCp/0tx
b7pR5cVr0kZ5bTZjiysEEprkG2ofAlXzj09VGtTfM8gQvCz9Puj7pGzw2iaIEQVk
hSGjoRWlI5x6+o16JOTHXzv9cYRUfDX6tjw3nQJIeMipuUkR8pkHUFjG3EeJEtO3
X/GO0G8rwUPaZiskGPiMZj7XqoVclnYL7JtntwUHR/dU5A/EhDfhgEfTXTqT78Oe
cKri+VJE+G/hYxbP0FNYaDtqIwJcX1tsy4HOpKVBncc+K/PvXElVsyQET/+uwH7p
Wm5ymndnuLoiQrWIA4nJC6rVwR4GPijuN0NCKcVdE+8jlOCBs3VBJTWKuu0J80RP
71iZy03AoK1YY4+nHglmE9HetAgSsbGh2fWC7DUS/4JzLSzOBeb+nn74zfmIfMU+
qUArFXvVGAtjmZZ/63cWzXDMZsp1BZ+O5dx6Gi2QtjgGYhh6DhW7ocQYXDkAeN/O
K1Yzwq/G4AEQA0k0/1I+F0Rdlo41+7tOp+LMCOoZXqUzhM0ZQ2sf3QclubxLX9U=
-----END CERTIFICATE-----
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQChqfmG6uOG95Jb
7uRi6yxohJ8GOR3gi39yX6JB+Xdukvqxy2/vsjH1+CF1i8jKZZO0hJLGT+/GmKIc
1c0XUEjVoQvCNQHIaDTXiUXOGXfkQNKR0vtJH5ZOZn/tvYAKPniYPmHuF3TpAB6H
ouLpyIC55SXdK7pTEbmU7J1aBjugn3O56cu6FzjU1j/0QVUVGloxApLvv57bmINa
X9ygKsh/ug0lhV1RwYLJ9UX57m95FIlcofa98tCuoKi++G+sWsjopDXVmsiTbjZf
s72kcDUTRYKNZbRFRRETORdOVRHxlAIPEn4QFYn/3wVSNFvfeY0j8RI5YcPLU66B
atun6HU+YAs6z8Qc8S1EMElJdoyVeLCqLA07btICzKq2I16TZAOWVng2P7NOtibA
eCzDAxAxJ3Oby+BVikKcu8WmJLxGvRvaPljdD76xjPB5NK6O0J62C3uU3EWhPODX
9H5l/WF+aNRqSccgs0Umddj33N+b/mTJnHn1GpanThrv1UfOFGKfxjemwESz66d1
iqD7iXvTxt7yZeU7LIMRgDqhVe6zoBpJEeWl9YYyfGPwgIOhwzNVZ5WkzQARs7si
3j3Wkmyca7hEN8qq8DkLWNf1PTcIwo/239wKRbyW3Z+U4IGRrVMdeSoC2JpRAx/e
EXTjuUePQlHCvwW9iiY7jTjDfbIvpwIDAQABAoICAC3CJMTRe3FaZezro210T2+O
Ck0CobhLA9nlw9GUwP9lTtxATwCzmXybrSzOUhknwzUXSUwkmCPIVCqBQbnVmagO
G3vu8QA+rqZLTpzVjJ/o0TFBXKsH681pKdCrELDVmeDN135C2W6SABI4Qq4VeIol
mCAQHn8gxzyl9Kvkk8AVIfZ/fJDBve5Qbm2+iEye1uSEa/68aEST2Kod9B7JvVKZ
4Nq78vwPH+v2JsZlfNvyuiakGWkOb47eHqVfQIyybaebwzkgxKEmUvGnuIfw0rUP
ubI4FVx9/iVIxZYAckHEuQh3HYOD9TmdcK4h79dDWnXP6G6hg3/rwbsT+fR+0aBQ
9rkKnA4uToGikYmplixAQ/jDBwMs3VQqenO+YBIsC4HEZ0fJUbs+l4LEnuUJxYcR
UlAvnVQXa1WGne3Yzb2xONWeiocKfhcdJ2JuQo00UR74+2Qonxn/WpimvlLCBDgI
uKxHCSWOgv5yPpU2kwTPIjORXcy/y2G9K2bnsQCzznPRDyNkZmavQxxG6greFcrO
/0yhRPuBgxKBRvXPO+F5fybKFlU9IPLFehV60jLUybBejab/lMJyxdkh9UMu2Xqy
FVsRGazJt6T6AGp6TFEEcFUQw7qXNhVo9S7zGGaJFJdYc+Vx8QJRoCe8EAYVH7Mp
b/eYGhHaKg6iG7QCjPPxAoIBAQDN54wtuDqpAA+4PmqhiEhQKhabNqAoVmAWUxnJ
Db4Zzvkkc3Fo/Yg0HnQVaT0KmkcxY7397lTdtiwNkWPgJ0f6+g7L4K7PA7xh/q84
IoXFGvYWwVdiVXLR1l06jorpA20clnba6CsbezwcllTq4bWvNnrAcM8l1YrAlRnV
qqqbPL78Rnba4C8q+VFy8r0d9OGnbvFcV7VWJjhr0a3aZbHQ67jPinNiUWvBVFFx
yGrqPMjkeHyiTLMhqQpaSHH67S88rj0g9RKexBaSUrl18QO7xnQHHSCcFWMQOiSN
shNvFri48dnU+Ms6ZLc3MBHbTK6uzP8xJCVnmsz/MWPGkQZFAoIBAQDI/vj/3/y/
EpIawyHN7PQAMoto4AQF6sVasrgGd1tRsJnGKrCugH9gILvyke3L7qg0JTV3bDJY
e8+vH1vC3NV7PsOlCFjMtRWG0lRbCh/b7Qe3pCvPu4mbFhJgMT/mz+vbl5zvcdgX
kvne+St/267NKnY5gHBDhqitBwkZwNlTWJ0zVmTecKXn/KwjS9lX1qU3HiT3UFkd
5Y5Nt5lj1IOK/6NCXkxVkgOc4Zjcxx138Cg03VJhIiHTusRq6z9iTSTDubhkaSbi
2nadptFBiQtkVhAJ5G53U7pl/pIhhiJy901bu/v/wrIMJ2l6hiZIcLrbg6VGXxjV
5dB7LDEtKoL7AoIBAQC8+ffA+mX0N9c1nSuWh5L+6DIJUHBbtTLJKonu6gsAeuJU
3xNGbfK1CwI1qHnaolAW91knlrcTKaBy726ACu1YXmp4GgW2f9JFCk/csGqfxaf4
qIg/+va/ugOku7CoPXnGFB6PuSffOBKqlhrn3DI41kKBHsgwDDYlnHKylMmyYmVS
+oUZS0pfIaXsXvbNaLQ2TG9+9gy7Pabo5e+vE0jI25+p84MEyH+iV3XMfUoLI7Cp
aB/TgZuimBelVvotd8Sz56K4/dSSHJwuvXfz1Dk9/Nz+rnAAcOyTtxlXZwnJGkx9
iZMIkTNMq6UwJJEu+ckVK5ZHjso5tWzSBo1xcCcVAoIBAQCPL0x1A7zK5VDd7cqE
J1w/U8KKiKN1D6VeElkUiiysyjERwdGxzmpvMYKSsDCGCdMbqrInDBXlgPYXnDBD
ZgxSywiW5ZZU5l+advWPEWxWwMmxoitvxfqmV5fpnMwYAmDUQ3KSBTjaumJ03G6H
nBkvoSMtnXjcMe6xrIRoK0Dmpgb+znn3GKqn1BFQ57TCZW+3DytoX33M1X6FkNie
DINVHv3Pxtt8ThNyzCeYh+RPT+9kkZIhDi6o5bENNd8miSw6nnBkX6BLFTRQ5MjH
dfh+luzAD1I+gZAVHsA9T4/09IXQZt+DeNBb5iu3FB/rlRsYS/UOZ6qKnjfhtz6l
HVbHAoIBAFjNY/UPJDxQ/uG+rMU0nrmSBRGdgBvQkcefjWX/LIZV3MjNilUQ+B2a
lXz5AHGmHRnnwQsBVfN8rf4qQLln8l34Kgm7+cIFavgfg2oqVbNyNgezSlUmRq0J
Ttf3xYJtRgRUx8F+BcgJXMqlNGTMQJY8wawM/ATkwkbmSwGOKe04sBeIkwEycMId
BupvfN5lxDrKqJVPSl1t5Rh4us95CNh22/c5Tq5rsynl02ZB4swlcsVTdv8FSGmM
QVf/MkWXGN/x4lHJhKyklHMGv15GGvys1nlPTstMfUYs55ioWRW46TXQ8vOyzzpg
67xzBKYFEde+hgYk7X1Xeqj8A6bsqro=
-----END PRIVATE KEY-----
@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFxzCCA6+gAwIBAgIJALnlnf5uzTkIMA0GCSqGSIb3DQEBCwUAMEsxCzAJBgNV
BAYTAkRFMRcwFQYDVQQKEw5zY2hva29rZWtzLm9yZzEjMCEGCSqGSIb3DQEJARYU
aGFubm9Ac2Nob2tva2Vrcy5vcmcwHhcNMTAwMTI3MDAyMTI1WhcNMjAwMTI1MDAy
MTI1WjBLMQswCQYDVQQGEwJERTEXMBUGA1UEChMOc2Nob2tva2Vrcy5vcmcxIzAh
BgkqhkiG9w0BCQEWFGhhbm5vQHNjaG9rb2tla3Mub3JnMIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEApJ4ODPwEooMW35dQPlBqdvcfkEvjhcsA7jmJfFqN
e/1T34zT44X9+KnMBSG2InacbD7eyFgjfaENFsZ87YkEBDIFZ/SHotLJZORQ8PUj
YoxPG4mjKN+yL2WthNcYbRyJreTbbDroNMuw6tkTSxeSXyYFQrKMCUfErVbZa/d5
RvfFVk+Au9dVUFhed/Stn5cv+a0ffvpyA7ygihm1kMFICbvPeI0846tmC2Ph7rM5
pYQyNBDOVpULODTk5Wu6jiiJJygvJWCZ1FdpsdBs5aKWHWdRhX++quGuflTTjH5d
qaIka4op9H7XksYphTDXmV+qHnva5jbPogwutDQcVsGBQcJaLmQqhsQK13bf4khE
iWJvfBLfHn8OOpY25ZwwuigJIwifNCxQeeT1FrLmyuYNhz2phPpzx065kqSUSR+A
Iw8DPE6e65UqMDKqZnID3dQeiQaFrHEV+Ibo0U/tD0YSBw5p33TMh0Es33IBWMac
m7x4hIFWdhl8W522u6qOrTswY3s8vB7blNWqMc9n7oWH8ybFf7EgKeDVtEN9AyBE
0WotXIEZWI+WvDbU1ACJXau9sQhYP/eerg7Zwr3iGUy4IQ5oUJibnjtcE+z8zmDN
pE6YcMCLJyLjXiQ3iHG9mNXzw7wPnslTbEEEukrfSlHGgW8Dm+VrNyW0JUM1bntx
vbMCAwEAAaOBrTCBqjAdBgNVHQ4EFgQUCedv7pDTuXtCxm4HTw9hUtrTvsowewYD
VR0jBHQwcoAUCedv7pDTuXtCxm4HTw9hUtrTvsqhT6RNMEsxCzAJBgNVBAYTAkRF
MRcwFQYDVQQKEw5zY2hva29rZWtzLm9yZzEjMCEGCSqGSIb3DQEJARYUaGFubm9A
c2Nob2tva2Vrcy5vcmeCCQC55Z3+bs05CDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4ICAQBHKAxA7WA/MEFjet03K8ouzEOr6Jrk2fZOuRhoDZ+9gr4FtaJB
P3Hh5D00kuSOvDnwsvCohxeNd1KTMAwVmVoH+NZkHERn3UXniUENlp18koI1ehlr
CZbXbzzE9Te9BelliSFA63q0cq0yJN1x9GyabU34XkAouCAmOqfSpKNZWZHGBHPF
bbYnZrHEMcsye6vKeTOcg1GqUHGrQM2WK0QaOwnCQv2RblI9VN+SeRoUJ44qTXdW
TwIYStsIPesacNcAQTStnHgKqIPx4zCwdx5xo8zONbXJfocqwyFqiAofvb9dN1nW
g1noVBcXB+oRBZW5CjFw87U88itq39i9+BWl835DWLBW2pVmx1QTLGv0RNgs/xVx
mWnjH4nNHvrjn6pRmqHZTk/SS0Hkl2qtDsynVxIl8EiMTfWSU3DBTuD2J/RSzuOE
eKtAbaoXkXE31jCl4FEZLITIZd8UkXacb9rN304tAK92L76JOAV+xOZxFRipmvx4
+A9qQXgLhtP4VaDajb44V/kCKPSA0Vm3apehke9Wl8dDtagfos1e6MxSu3EVLXRF
SP2U777V77pdMSd0f/7cerKn5FjrxW1v1FaP1oIGniMk4qQNTgA/jvvhjybsPlVA
jsfnhWGbh1voJa0RQcMiRMsxpw2P1KNOEu37W2eq/vFghVztZJQUmb5iNw==
-----END CERTIFICATE-----
@@ -0,0 +1,4 @@
from gevent import GreenletExit
assert issubclass(GreenletExit, BaseException)
assert not issubclass(GreenletExit, Exception)
@@ -0,0 +1,159 @@
# Copyright 2018 gevent contributors. See LICENSE for details.
import os
import unittest
import sys
from gevent import _config
class TestResolver(unittest.TestCase):
old_resolver = None
def setUp(self):
if 'GEVENT_RESOLVER' in os.environ:
self.old_resolver = os.environ['GEVENT_RESOLVER']
del os.environ['GEVENT_RESOLVER']
def tearDown(self):
if self.old_resolver:
os.environ['GEVENT_RESOLVER'] = self.old_resolver
def test_key(self):
self.assertEqual(_config.Resolver.environment_key, 'GEVENT_RESOLVER')
def test_default(self):
from gevent.resolver.thread import Resolver
conf = _config.Resolver()
self.assertEqual(conf.get(), Resolver)
def test_env(self):
from gevent.resolver.blocking import Resolver
os.environ['GEVENT_RESOLVER'] = 'foo,bar,block,dnspython'
conf = _config.Resolver()
self.assertEqual(conf.get(), Resolver)
os.environ['GEVENT_RESOLVER'] = 'dnspython'
# The existing value is unchanged
self.assertEqual(conf.get(), Resolver)
# A new object reflects it
try:
from gevent.resolver.dnspython import Resolver as DResolver
except ImportError: # pragma: no cover
# dnspython is optional; skip it.
import warnings
warnings.warn('dnspython not installed')
else:
conf = _config.Resolver()
self.assertEqual(conf.get(), DResolver)
def test_set_str_long(self):
from gevent.resolver.blocking import Resolver
conf = _config.Resolver()
conf.set('gevent.resolver.blocking.Resolver')
self.assertEqual(conf.get(), Resolver)
def test_set_str_short(self):
from gevent.resolver.blocking import Resolver
conf = _config.Resolver()
conf.set('block')
self.assertEqual(conf.get(), Resolver)
def test_set_class(self):
from gevent.resolver.blocking import Resolver
conf = _config.Resolver()
conf.set(Resolver)
self.assertEqual(conf.get(), Resolver)
def test_set_through_config(self):
from gevent.resolver.thread import Resolver as Default
from gevent.resolver.blocking import Resolver
conf = _config.Config()
self.assertEqual(conf.resolver, Default)
conf.resolver = 'block'
self.assertEqual(conf.resolver, Resolver)
class TestFunctions(unittest.TestCase):
def test_validate_bool(self):
self.assertTrue(_config.validate_bool('on'))
self.assertTrue(_config.validate_bool('1'))
self.assertFalse(_config.validate_bool('off'))
self.assertFalse(_config.validate_bool('0'))
self.assertFalse(_config.validate_bool(''))
with self.assertRaises(ValueError):
_config.validate_bool(' hmm ')
def test_validate_invalid(self):
with self.assertRaises(ValueError):
_config.validate_invalid(self)
class TestConfig(unittest.TestCase):
def test__dir__(self):
self.assertEqual(sorted(_config.config.settings),
sorted(dir(_config.config)))
def test_getattr(self):
# Bypass the property that might be set here
self.assertIsNotNone(_config.config.__getattr__('resolver'))
def test__getattr__invalid(self):
with self.assertRaises(AttributeError):
getattr(_config.config, 'no_such_setting')
def test_set_invalid(self):
with self.assertRaises(AttributeError):
_config.config.set('no such setting', True)
class TestImportableSetting(unittest.TestCase):
def test_empty_list(self):
i = _config.ImportableSetting()
with self.assertRaisesRegex(ImportError,
"Cannot import from empty list"):
i._import_one_of([])
def test_path_not_supported(self):
import warnings
i = _config.ImportableSetting()
path = list(sys.path)
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
with self.assertRaisesRegex(ImportError,
"Cannot import 'foo/bar/gevent.no_such_module'"):
i._import_one('foo/bar/gevent.no_such_module')
# We restored the path
self.assertEqual(path, sys.path)
# We did not issue a warning
self.assertEqual(len(w), 0)
def test_non_string(self):
i = _config.ImportableSetting()
self.assertIs(i._import_one(self), self)
def test_get_options(self):
i = _config.ImportableSetting()
self.assertEqual({}, i.get_options())
i.shortname_map = {'foo': 'bad/path'}
options = i.get_options()
self.assertIn('foo', options)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
# copyright 2018 gevent contributors. See LICENSE for details.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import gc
import gevent.testing as greentest
from gevent._ident import IdentRegistry
from gevent._compat import PYPY
class Target(object):
pass
class TestIdent(greentest.TestCase):
def setUp(self):
self.reg = IdentRegistry()
def tearDown(self):
self.reg = None
def test_basic(self):
target = Target()
self.assertEqual(0, self.reg.get_ident(target))
self.assertEqual(1, len(self.reg))
self.assertEqual(0, self.reg.get_ident(target))
self.assertEqual(1, len(self.reg))
target2 = Target()
self.assertEqual(1, self.reg.get_ident(target2))
self.assertEqual(2, len(self.reg))
self.assertEqual(1, self.reg.get_ident(target2))
self.assertEqual(2, len(self.reg))
self.assertEqual(0, self.reg.get_ident(target))
# When an object dies, we can re-use
# its id. Under PyPy we need to collect garbage first.
del target
if PYPY:
for _ in range(3):
gc.collect()
self.assertEqual(1, len(self.reg))
target3 = Target()
self.assertEqual(1, self.reg.get_ident(target2))
self.assertEqual(0, self.reg.get_ident(target3))
self.assertEqual(2, len(self.reg))
@greentest.skipOnPyPy("This would need to GC very frequently")
def test_circle(self):
keep_count = 3
keepalive = [None] * keep_count
for i in range(1000):
target = Target()
# Drop an old one.
keepalive[i % keep_count] = target
self.assertLessEqual(self.reg.get_ident(target), keep_count)
@greentest.skipOnPurePython("Needs C extension")
class TestCExt(greentest.TestCase):
def test_c_extension(self):
self.assertEqual(IdentRegistry.__module__,
'gevent._gevent_c_ident')
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,386 @@
# Copyright 2018 gevent contributors. See LICENSE for details.
import gc
import unittest
from greenlet import gettrace
from greenlet import settrace
from gevent.monkey import get_original
from gevent._compat import thread_mod_name
from gevent._compat import NativeStrIO
from gevent.testing import verify
from gevent.testing.skipping import skipWithoutPSUtil
from gevent import _monitor as monitor
from gevent import config as GEVENT_CONFIG
get_ident = get_original(thread_mod_name, 'get_ident')
class MockHub(object):
_threadpool = None
_resolver = None
def __init__(self):
self.thread_ident = get_ident()
self.exception_stream = NativeStrIO()
self.dead = False
def __bool__(self):
return not self.dead
__nonzero__ = __bool__
def handle_error(self, *args): # pylint:disable=unused-argument
raise # pylint:disable=misplaced-bare-raise
@property
def loop(self):
return self
def reinit(self):
"mock loop.reinit"
class _AbstractTestPeriodicMonitoringThread(object):
# Makes sure we don't actually spin up a new monitoring thread.
# pylint:disable=no-member
def setUp(self):
super(_AbstractTestPeriodicMonitoringThread, self).setUp()
self._orig_start_new_thread = monitor.start_new_thread
self._orig_thread_sleep = monitor.thread_sleep
monitor.thread_sleep = lambda _s: gc.collect() # For PyPy
self.tid = 0xDEADBEEF
def start_new_thread(_f, _a):
r = self.tid
self.tid += 1
return r
monitor.start_new_thread = start_new_thread
self.hub = MockHub()
self.pmt = monitor.PeriodicMonitoringThread(self.hub)
self.hub.periodic_monitoring_thread = self.pmt
self.pmt_default_funcs = self.pmt.monitoring_functions()[:]
self.len_pmt_default_funcs = len(self.pmt_default_funcs)
def tearDown(self):
monitor.start_new_thread = self._orig_start_new_thread
monitor.thread_sleep = self._orig_thread_sleep
prev = self.pmt._greenlet_tracer.previous_trace_function
self.pmt.kill()
assert gettrace() is prev, (gettrace(), prev)
settrace(None)
super(_AbstractTestPeriodicMonitoringThread, self).tearDown()
class TestPeriodicMonitoringThread(_AbstractTestPeriodicMonitoringThread,
unittest.TestCase):
def test_constructor(self):
self.assertEqual(0xDEADBEEF, self.pmt.monitor_thread_ident)
self.assertEqual(gettrace(), self.pmt._greenlet_tracer)
@skipWithoutPSUtil("Verifies the process")
def test_get_process(self):
proc = self.pmt._get_process()
self.assertIsNotNone(proc)
# Same object is returned each time.
self.assertIs(proc, self.pmt._get_process())
def test_hub_wref(self):
self.assertIs(self.hub, self.pmt.hub)
del self.hub
gc.collect()
self.assertIsNone(self.pmt.hub)
# And it killed itself.
self.assertFalse(self.pmt.should_run)
self.assertIsNone(gettrace())
def test_add_monitoring_function(self):
self.assertRaises(ValueError, self.pmt.add_monitoring_function, None, 1)
self.assertRaises(ValueError, self.pmt.add_monitoring_function, lambda: None, -1)
def f():
"Does nothing"
# Add
self.pmt.add_monitoring_function(f, 1)
self.assertEqual(self.len_pmt_default_funcs + 1, len(self.pmt.monitoring_functions()))
self.assertEqual(1, self.pmt.monitoring_functions()[1].period)
# Update
self.pmt.add_monitoring_function(f, 2)
self.assertEqual(self.len_pmt_default_funcs + 1, len(self.pmt.monitoring_functions()))
self.assertEqual(2, self.pmt.monitoring_functions()[1].period)
# Remove
self.pmt.add_monitoring_function(f, None)
self.assertEqual(self.len_pmt_default_funcs, len(self.pmt.monitoring_functions()))
def test_calculate_sleep_time(self):
self.assertEqual(
self.pmt.monitoring_functions()[0].period,
self.pmt.calculate_sleep_time())
# Pretend that GEVENT_CONFIG.max_blocking_time was set to 0,
# to disable this monitor.
self.pmt._calculated_sleep_time = 0
self.assertEqual(
self.pmt.inactive_sleep_time,
self.pmt.calculate_sleep_time()
)
# Getting the list of monitoring functions will also
# do this, if it looks like it has changed
self.pmt.monitoring_functions()[0].period = -1
self.pmt._calculated_sleep_time = 0
self.pmt.monitoring_functions()
self.assertEqual(
self.pmt.monitoring_functions()[0].period,
self.pmt.calculate_sleep_time())
self.assertEqual(
self.pmt.monitoring_functions()[0].period,
self.pmt._calculated_sleep_time)
def test_call_destroyed_hub(self):
# Add a function that destroys the hub so we break out (eventually)
# This clears the wref, which eventually calls kill()
def f(_hub):
_hub = None
self.hub = None
gc.collect()
self.pmt.add_monitoring_function(f, 0.1)
self.pmt()
self.assertFalse(self.pmt.should_run)
def test_call_dead_hub(self):
# Add a function that makes the hub go false (e.g., it quit)
# This causes the function to kill itself.
def f(hub):
hub.dead = True
self.pmt.add_monitoring_function(f, 0.1)
self.pmt()
self.assertFalse(self.pmt.should_run)
def test_call_SystemExit(self):
# breaks the loop
def f(_hub):
raise SystemExit()
self.pmt.add_monitoring_function(f, 0.1)
self.pmt()
def test_call_other_error(self):
class MyException(Exception):
pass
def f(_hub):
raise MyException()
self.pmt.add_monitoring_function(f, 0.1)
with self.assertRaises(MyException):
self.pmt()
def test_hub_reinit(self):
import os
from gevent.hub import reinit
self.pmt.pid = -1
old_tid = self.pmt.monitor_thread_ident
reinit(self.hub)
self.assertEqual(os.getpid(), self.pmt.pid)
self.assertEqual(old_tid + 1, self.pmt.monitor_thread_ident)
class TestPeriodicMonitorBlocking(_AbstractTestPeriodicMonitoringThread,
unittest.TestCase):
def test_previous_trace(self):
self.pmt.kill()
self.assertIsNone(gettrace())
called = []
def f(*args):
called.append(args)
settrace(f)
self.pmt = monitor.PeriodicMonitoringThread(self.hub)
self.assertEqual(gettrace(), self.pmt._greenlet_tracer)
self.assertIs(self.pmt._greenlet_tracer.previous_trace_function, f)
self.pmt._greenlet_tracer('event', ('args',))
self.assertEqual([('event', ('args',))], called)
def test__greenlet_tracer(self):
self.assertEqual(0, self.pmt._greenlet_tracer.greenlet_switch_counter)
# Unknown event still counts as a switch (should it?)
self.pmt._greenlet_tracer('unknown', None)
self.assertEqual(1, self.pmt._greenlet_tracer.greenlet_switch_counter)
self.assertIsNone(self.pmt._greenlet_tracer.active_greenlet)
origin = object()
target = object()
self.pmt._greenlet_tracer('switch', (origin, target))
self.assertEqual(2, self.pmt._greenlet_tracer.greenlet_switch_counter)
self.assertIs(target, self.pmt._greenlet_tracer.active_greenlet)
# Unknown event removes active greenlet
self.pmt._greenlet_tracer('unknown', ())
self.assertEqual(3, self.pmt._greenlet_tracer.greenlet_switch_counter)
self.assertIsNone(self.pmt._greenlet_tracer.active_greenlet)
def test_monitor_blocking(self):
# Initially there's no active greenlet and no switches,
# so nothing is considered blocked
from gevent.events import subscribers
from gevent.events import IEventLoopBlocked
events = []
subscribers.append(events.append)
self.assertFalse(self.pmt.monitor_blocking(self.hub))
# Give it an active greenlet
origin = object()
target = object()
self.pmt._greenlet_tracer('switch', (origin, target))
# We've switched, so we're not blocked
self.assertFalse(self.pmt.monitor_blocking(self.hub))
self.assertFalse(events)
# Again without switching is a problem.
self.assertTrue(self.pmt.monitor_blocking(self.hub))
self.assertTrue(events)
verify.verifyObject(IEventLoopBlocked, events[0])
del events[:]
# But we can order it not to be a problem
self.pmt.ignore_current_greenlet_blocking()
self.assertFalse(self.pmt.monitor_blocking(self.hub))
self.assertFalse(events)
# And back again
self.pmt.monitor_current_greenlet_blocking()
self.assertTrue(self.pmt.monitor_blocking(self.hub))
# A bad thread_ident in the hub doesn't mess things up
self.hub.thread_ident = -1
self.assertTrue(self.pmt.monitor_blocking(self.hub))
class MockProcess(object):
def __init__(self, rss):
self.rss = rss
def memory_full_info(self):
return self
@skipWithoutPSUtil("Accessess memory info")
class TestPeriodicMonitorMemory(_AbstractTestPeriodicMonitoringThread,
unittest.TestCase):
rss = 0
def setUp(self):
_AbstractTestPeriodicMonitoringThread.setUp(self)
self._old_max = GEVENT_CONFIG.max_memory_usage
GEVENT_CONFIG.max_memory_usage = None
self.pmt._get_process = lambda: MockProcess(self.rss)
def tearDown(self):
GEVENT_CONFIG.max_memory_usage = self._old_max
_AbstractTestPeriodicMonitoringThread.tearDown(self)
def test_can_monitor_and_install(self):
# We run tests with psutil installed, and we have access to our
# process.
self.assertTrue(self.pmt.can_monitor_memory_usage())
# No warning, adds a function
self.pmt.install_monitor_memory_usage()
self.assertEqual(self.len_pmt_default_funcs + 1, len(self.pmt.monitoring_functions()))
def test_cannot_monitor_and_install(self):
import warnings
self.pmt._get_process = lambda: None
self.assertFalse(self.pmt.can_monitor_memory_usage())
# This emits a warning, visible by default
with warnings.catch_warnings(record=True) as ws:
self.pmt.install_monitor_memory_usage()
self.assertEqual(1, len(ws))
self.assertIs(monitor.MonitorWarning, ws[0].category)
def test_monitor_no_allowed(self):
self.assertEqual(-1, self.pmt.monitor_memory_usage(None))
def test_monitor_greater(self):
from gevent import events
self.rss = 2
GEVENT_CONFIG.max_memory_usage = 1
# Initial event
event = self.pmt.monitor_memory_usage(None)
self.assertIsInstance(event, events.MemoryUsageThresholdExceeded)
self.assertEqual(2, event.mem_usage)
self.assertEqual(1, event.max_allowed) # pylint:disable=no-member
self.assertIsInstance(event.memory_info, MockProcess) # pylint:disable=no-member
# No growth, no event
event = self.pmt.monitor_memory_usage(None)
self.assertIsNone(event)
# Growth, event
self.rss = 3
event = self.pmt.monitor_memory_usage(None)
self.assertIsInstance(event, events.MemoryUsageThresholdExceeded)
self.assertEqual(3, event.mem_usage)
# Shrinking below gets us back
self.rss = 1
event = self.pmt.monitor_memory_usage(None)
self.assertIsInstance(event, events.MemoryUsageUnderThreshold)
self.assertEqual(1, event.mem_usage)
# coverage
repr(event)
# No change, no event
event = self.pmt.monitor_memory_usage(None)
self.assertIsNone(event)
# Growth, event
self.rss = 3
event = self.pmt.monitor_memory_usage(None)
self.assertIsInstance(event, events.MemoryUsageThresholdExceeded)
self.assertEqual(3, event.mem_usage)
def test_monitor_initial_below(self):
self.rss = 1
GEVENT_CONFIG.max_memory_usage = 10
event = self.pmt.monitor_memory_usage(None)
self.assertIsNone(event)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,102 @@
import sys
import os
import glob
import atexit
# subprocess: include in subprocess tests
from gevent.testing import util
from gevent.testing import sysinfo
from gevent.testing.support import is_resource_enabled
TIMEOUT = 120
# XXX: Generalize this so other packages can use it.
def get_absolute_pythonpath():
paths = [os.path.abspath(p) for p in os.environ.get('PYTHONPATH', '').split(os.pathsep)]
return os.pathsep.join(paths)
def TESTRUNNER(tests=None):
if not is_resource_enabled('gevent_monkey'):
util.log('WARNING: Testing monkey-patched stdlib has been disabled',
color="suboptimal-behaviour")
return
try:
test_dir, version_test_dir = util.find_stdlib_tests()
except util.NoSetupPyFound as e:
util.log("WARNING: No setup.py and src/greentest found: %r", e,
color="suboptimal-behaviour")
return
if not os.path.exists(test_dir):
util.log('WARNING: No test directory found at %s', test_dir,
color="suboptimal-behaviour")
return
# pylint:disable=unspecified-encoding
with open(os.path.join(test_dir, 'version')) as f:
preferred_version = f.read().strip()
running_version = sysinfo.get_python_version()
if preferred_version != running_version:
util.log('WARNING: The tests in %s/ are from version %s and your Python is %s',
test_dir, preferred_version, running_version,
color="suboptimal-behaviour")
version_tests = glob.glob('%s/test_*.py' % version_test_dir)
version_tests = sorted(version_tests)
if not tests:
tests = glob.glob('%s/test_*.py' % test_dir)
tests = sorted(tests)
PYTHONPATH = (os.getcwd() + os.pathsep + get_absolute_pythonpath()).rstrip(':')
tests = sorted(set(os.path.basename(x) for x in tests))
version_tests = sorted(set(os.path.basename(x) for x in version_tests))
util.log("Discovered %d tests in %s", len(tests), test_dir)
util.log("Discovered %d version-specific tests in %s", len(version_tests), version_test_dir)
options = {
'cwd': test_dir,
'timeout': TIMEOUT,
'setenv': {
'PYTHONPATH': PYTHONPATH,
# debug produces resource tracking warnings for the
# CFFI backends. On Python 2, many of the stdlib tests
# rely on refcounting to close sockets so they produce
# lots of noise. Python 3 is not completely immune;
# test_ftplib.py tends to produce warnings---and the Python 3
# test framework turns those into test failures!
'GEVENT_DEBUG': 'error',
}
}
if tests and not sys.platform.startswith("win"):
atexit.register(os.system, 'rm -f */@test*')
basic_args = [sys.executable, '-u', '-W', 'ignore', '-m', 'gevent.testing.monkey_test']
for filename in tests:
if filename in version_tests:
util.log("Overriding %s from %s with file from %s", filename, test_dir, version_test_dir)
continue
yield basic_args + [filename], options.copy()
options['cwd'] = version_test_dir
for filename in version_tests:
yield basic_args + [filename], options.copy()
def main():
from gevent.testing import testrunner
discovered_tests = TESTRUNNER(sys.argv[1:])
discovered_tests = list(discovered_tests)
return testrunner.Runner(discovered_tests, quiet=None)()
if __name__ == '__main__':
main()
@@ -0,0 +1,322 @@
# Check __all__, __implements__, __extensions__, __imports__ of the modules
import functools
import sys
import unittest
import types
import importlib
import warnings
from gevent.testing import modules
from gevent.testing.sysinfo import PLATFORM_SPECIFIC_SUFFIXES
from gevent.testing.util import debug
from gevent._patcher import MAPPING
class ANY:
def __contains__(self, item):
return True
ANY = ANY()
NOT_IMPLEMENTED = {
'socket': ['CAPI'],
'thread': ['allocate', 'exit_thread', 'interrupt_main', 'start_new'],
'select': ANY,
'os': ANY,
'threading': ANY,
'builtins': ANY,
'signal': ANY,
}
COULD_BE_MISSING = {
'socket': ['create_connection', 'RAND_add', 'RAND_egd', 'RAND_status'],
'subprocess': ['_posixsubprocess'],
}
# Things without an __all__ should generally be internal implementation
# helpers
NO_ALL = {
'gevent.threading',
'gevent._compat',
'gevent._corecffi',
'gevent._ffi',
'gevent._fileobjectcommon',
'gevent._fileobjectposix',
'gevent._patcher',
'gevent._socketcommon',
'gevent._tblib',
'gevent._util',
'gevent.resolver._addresses',
'gevent.resolver._hostsfile',
# gevent.monkey is handled specially.
}
ALLOW_IMPLEMENTS = [
'gevent._queue',
# 'gevent.resolver.dnspython',
# 'gevent.resolver_thread',
# 'gevent.resolver.blocking',
# 'gevent.resolver_ares',
# 'gevent.server',
# 'gevent._resolver.hostfile',
# 'gevent.util',
# 'gevent.threadpool',
# 'gevent.timeout',
]
# A list of modules that may contain things that aren't actually, technically,
# extensions, but that need to be in __extensions__ anyway due to the way,
# for example, monkey patching, needs to work.
EXTRA_EXTENSIONS = []
if sys.platform.startswith('win'):
EXTRA_EXTENSIONS.append('gevent.signal')
_MISSING = '<marker object>'
def skip_if_no_stdlib_counterpart(f):
@functools.wraps(f)
def m(self):
if not self.stdlib_module:
self.skipTest("Need stdlib counterpart to %s" % self.modname)
f(self)
return m
class AbstractTestMixin:
modname = None
stdlib_has_all = False
stdlib_all = None
stdlib_name = None
stdlib_module = None
@classmethod
def setUpClass(cls):
modname = cls.modname
if modname.endswith(PLATFORM_SPECIFIC_SUFFIXES):
raise unittest.SkipTest("Module %s is platform specific" % modname)
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
try:
cls.module = importlib.import_module(modname)
except ImportError:
if modname in modules.OPTIONAL_MODULES:
msg = "Unable to import %s" % modname
raise unittest.SkipTest(msg)
raise
cls.__implements__ = getattr(cls.module, '__implements__', None)
cls.__imports__ = getattr(cls.module, '__imports__', [])
cls.__extensions__ = getattr(cls.module, '__extensions__', [])
cls.stdlib_name = MAPPING.get(modname)
if cls.stdlib_name is not None:
try:
cls.stdlib_module = __import__(cls.stdlib_name)
except ImportError:
pass
else:
cls.stdlib_has_all = True
cls.stdlib_all = getattr(cls.stdlib_module, '__all__', None)
if cls.stdlib_all is None:
cls.stdlib_has_all = False
cls.stdlib_all = [
name
for name in dir(cls.stdlib_module)
if not name.startswith('_')
and not isinstance(getattr(cls.stdlib_module, name), types.ModuleType)
]
def skipIfNoAll(self):
if not hasattr(self.module, '__all__'):
self.assertTrue(
self.modname in NO_ALL or self.modname.startswith('gevent.monkey.'),
"Module has no all"
)
self.skipTest("%s Needs __all__" % self.modname)
def test_all(self):
# Check that __all__ is present in the gevent module,
# and only includes things that actually exist and can be
# imported from it.
self.skipIfNoAll()
names = {}
exec("from %s import *" % self.modname, names)
names.pop('__builtins__', None)
self.maxDiff = None
# It should match both as a set
self.assertEqual(set(names), set(self.module.__all__))
# and it should not contain duplicates.
self.assertEqual(sorted(names), sorted(self.module.__all__))
def test_all_formula(self):
self.skipIfNoAll()
# Check __all__ = __implements__ + __extensions__ + __imported__
# This is disabled because it was previously being skipped entirely
# back when we had to call things manually. In that time, it drifted
# out of sync. It should be enabled again and problems corrected.
all_calculated = (
tuple(self.__implements__ or ())
+ tuple(self.__imports__ or ())
+ tuple(self.__extensions__ or ())
)
try:
self.assertEqual(sorted(all_calculated),
sorted(self.module.__all__))
except AssertionError:
self.skipTest("Module %s fails the all formula; fix it" % self.modname)
except TypeError:
# TypeError: '<' not supported between instances of 'type' and 'str'
raise AssertionError(
"Unable to sort %r from all %s in %s" % (
all_calculated, self.module.__all__, self.module
)
)
def test_implements_presence_justified(self):
# Check that __implements__ is present only if the module is modeled
# after a module from stdlib (like gevent.socket).
if self.modname in ALLOW_IMPLEMENTS:
return
if self.__implements__ is not None and self.stdlib_module is None:
raise AssertionError(
'%s (%r) has __implements__ (%s) but no stdlib counterpart module exists (%s)'
% (self.modname, self.module, self.__implements__, self.stdlib_name))
@skip_if_no_stdlib_counterpart
def test_implements_subset_of_stdlib_all(self):
# Check that __implements__ + __imports__ is a subset of the
# corresponding standard module __all__ or dir()
for name in tuple(self.__implements__ or ()) + tuple(self.__imports__):
if name in self.stdlib_all:
continue
if name in COULD_BE_MISSING.get(self.stdlib_name, ()):
continue
if name in dir(self.stdlib_module): # like thread._local which is not in thread.__all__
continue
raise AssertionError('%r is not found in %r.__all__ nor in dir(%r)' % (name, self.stdlib_module, self.stdlib_module))
@skip_if_no_stdlib_counterpart
def test_implements_actually_implements(self):
# Check that the module actually implements the entries from
# __implements__
for name in self.__implements__ or ():
item = getattr(self.module, name)
try:
stdlib_item = getattr(self.stdlib_module, name)
self.assertIsNot(item, stdlib_item)
except AttributeError:
if name not in COULD_BE_MISSING.get(self.stdlib_name, []):
raise
@skip_if_no_stdlib_counterpart
def test_imports_actually_imports(self):
# Check that the module actually imports the entries from
# __imports__
for name in self.__imports__:
item = getattr(self.module, name)
stdlib_item = getattr(self.stdlib_module, name)
self.assertIs(item, stdlib_item)
@skip_if_no_stdlib_counterpart
def test_extensions_actually_extend(self):
# Check that the module actually defines new entries in
# __extensions__
if self.modname in EXTRA_EXTENSIONS:
return
for name in self.__extensions__:
try:
if hasattr(self.stdlib_module, name):
raise AssertionError("'%r' is not an extension, it is found in %r" % (
name, self.stdlib_module
))
except TypeError as ex:
# TypeError: attribute name must be string, not 'type'
raise AssertionError(
"Got TypeError (%r) getting %r (of %s) from %s/%s" % (
ex, name, self.__extensions__, self.stdlib_module, self.modname
)
)
@skip_if_no_stdlib_counterpart
def test_completeness(self): # pylint:disable=too-many-branches
# Check that __all__ (or dir()) of the corresponsing stdlib is
# a subset of __all__ of this module
missed = []
for name in self.stdlib_all:
if name not in getattr(self.module, '__all__', []):
missed.append(name)
# handle stuff like ssl.socket and ssl.socket_error which have no reason to be in gevent.ssl.__all__
if not self.stdlib_has_all:
for name in missed[:]:
if hasattr(self.module, name):
missed.remove(name)
# remove known misses
not_implemented = NOT_IMPLEMENTED.get(self.stdlib_name)
if not_implemented is not None:
result = []
for name in missed:
if name in not_implemented:
# We often don't want __all__ to be set because we wind up
# documenting things that we just copy in from the stdlib.
# But if we implement it, don't print a warning
if getattr(self.module, name, _MISSING) is _MISSING:
debug('IncompleteImplWarning: %s.%s' % (self.modname, name))
else:
result.append(name)
missed = result
if missed:
if self.stdlib_has_all:
msg = '''The following items
in %r.__all__
are missing from %r:
%r''' % (self.stdlib_module, self.module, missed)
else:
msg = '''The following items
in dir(%r)
are missing from %r:
%r''' % (self.stdlib_module, self.module, missed)
raise AssertionError(msg)
def _create_tests():
for _, modname in modules.walk_modules(include_so=False, recursive=True,
check_optional=False):
if modname.endswith(PLATFORM_SPECIFIC_SUFFIXES):
continue
if modname.endswith('__main__'):
# gevent.monkey.__main__ especially is a problem.
# These aren't meant to be imported anyway.
continue
orig_modname = modname
modname_no_period = orig_modname.replace('.', '_')
cls = type(
'Test_' + modname_no_period,
(AbstractTestMixin, unittest.TestCase),
{
'__module__': __name__,
'modname': orig_modname
}
)
globals()[cls.__name__] = cls
_create_tests()
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,132 @@
# Copyright (c) 2008 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import gevent.testing as greentest
import gevent
from gevent import util, socket
DELAY = 0.1
class Test(greentest.TestCase):
@greentest.skipOnAppVeyor("Timing causes the state to often be [start,finished]")
def test_killing_dormant(self):
state = []
def test():
try:
state.append('start')
gevent.sleep(DELAY * 3.0)
except: # pylint:disable=bare-except
state.append('except')
# catching GreenletExit
state.append('finished')
g = gevent.spawn(test)
gevent.sleep(DELAY / 2)
assert state == ['start'], state
g.kill()
# will not get there, unless switching is explicitly scheduled by kill
self.assertEqual(state, ['start', 'except', 'finished'])
def test_nested_with_timeout(self):
def func():
return gevent.with_timeout(0.2, gevent.sleep, 2, timeout_value=1)
self.assertRaises(gevent.Timeout, gevent.with_timeout, 0.1, func)
def test_sleep_invalid_switch(self):
p = gevent.spawn(util.wrap_errors(AssertionError, gevent.sleep), 2)
gevent.sleep(0) # wait for p to start, because actual order of switching is reversed
switcher = gevent.spawn(p.switch, None)
result = p.get()
assert isinstance(result, AssertionError), result
assert 'Invalid switch' in str(result), repr(str(result))
switcher.kill()
if hasattr(socket, 'socketpair'):
def _test_wait_read_invalid_switch(self, sleep):
sock1, sock2 = socket.socketpair()
try:
p = gevent.spawn(util.wrap_errors(AssertionError,
socket.wait_read), # pylint:disable=no-member
sock1.fileno())
gevent.get_hub().loop.run_callback(switch_None, p)
if sleep is not None:
gevent.sleep(sleep)
result = p.get()
assert isinstance(result, AssertionError), result
assert 'Invalid switch' in str(result), repr(str(result))
finally:
sock1.close()
sock2.close()
def test_invalid_switch_None(self):
self._test_wait_read_invalid_switch(None)
def test_invalid_switch_0(self):
self._test_wait_read_invalid_switch(0)
def test_invalid_switch_1(self):
self._test_wait_read_invalid_switch(0.001)
# we don't test wait_write the same way, because socket is always ready to write
def switch_None(g):
g.switch(None)
class TestTimers(greentest.TestCase):
def test_timer_fired(self):
lst = [1]
def func():
gevent.spawn_later(0.01, lst.pop)
gevent.sleep(0.02)
gevent.spawn(func)
# Func has not run yet
self.assertEqual(lst, [1])
# Run callbacks but don't yield.
gevent.sleep()
# Let timers fire. Func should be done.
gevent.sleep(0.1)
self.assertEqual(lst, [])
def test_spawn_is_not_cancelled(self):
lst = [1]
def func():
gevent.spawn(lst.pop)
# exiting immediately, but self.lst.pop must be called
gevent.spawn(func)
gevent.sleep(0.1)
self.assertEqual(lst, [])
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,210 @@
# Copyright (c) 2008 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import gevent.testing as greentest
import weakref
import time
import gc
from gevent import sleep
from gevent import Timeout
from gevent import get_hub
from gevent.testing.timing import SMALL_TICK as DELAY
from gevent.testing import flaky
class Error(Exception):
pass
class _UpdateNowProxy(object):
update_now_calls = 0
def __init__(self, loop):
self.loop = loop
def __getattr__(self, name):
return getattr(self.loop, name)
def update_now(self):
self.update_now_calls += 1
self.loop.update_now()
class _UpdateNowWithTimerProxy(_UpdateNowProxy):
def timer(self, *_args, **_kwargs):
return _Timer(self)
class _Timer(object):
pending = False
active = False
def __init__(self, loop):
self.loop = loop
def start(self, *_args, **kwargs):
if kwargs.get("update"):
self.loop.update_now()
self.pending = self.active = True
def stop(self):
self.active = self.pending = False
def close(self):
"Does nothing"
class Test(greentest.TestCase):
def test_timeout_calls_update_now(self):
hub = get_hub()
loop = hub.loop
proxy = _UpdateNowWithTimerProxy(loop)
hub.loop = proxy
try:
with Timeout(DELAY * 2) as t:
self.assertTrue(t.pending)
finally:
hub.loop = loop
self.assertEqual(1, proxy.update_now_calls)
def test_sleep_calls_update_now(self):
hub = get_hub()
loop = hub.loop
proxy = _UpdateNowProxy(loop)
hub.loop = proxy
try:
sleep(0.01)
finally:
hub.loop = loop
self.assertEqual(1, proxy.update_now_calls)
@greentest.skipOnAppVeyor("Timing is flaky, especially under Py 3.4/64-bit")
@greentest.skipOnPyPy3OnCI("Timing is flaky, especially under Py 3.4/64-bit")
@greentest.reraises_flaky_timeout((Timeout, AssertionError))
def test_api(self):
# Nothing happens if with-block finishes before the timeout expires
t = Timeout(DELAY * 2)
self.assertFalse(t.pending, t)
with t:
self.assertTrue(t.pending, t)
sleep(DELAY)
# check if timer was actually cancelled
self.assertFalse(t.pending, t)
sleep(DELAY * 2)
# An exception will be raised if it's not
with self.assertRaises(Timeout) as exc:
with Timeout(DELAY) as t:
sleep(DELAY * 10)
self.assertIs(exc.exception, t)
# You can customize the exception raised:
with self.assertRaises(IOError):
with Timeout(DELAY, IOError("Operation takes way too long")):
sleep(DELAY * 10)
# Providing classes instead of values should be possible too:
with self.assertRaises(ValueError):
with Timeout(DELAY, ValueError):
sleep(DELAY * 10)
try:
1 / 0
except ZeroDivisionError:
with self.assertRaises(ZeroDivisionError):
with Timeout(DELAY, sys.exc_info()[0]):
sleep(DELAY * 10)
raise AssertionError('should not get there')
raise AssertionError('should not get there')
else:
raise AssertionError('should not get there')
# It's possible to cancel the timer inside the block:
with Timeout(DELAY) as timer:
timer.cancel()
sleep(DELAY * 2)
# To silent the exception before exiting the block, pass False as second parameter.
XDELAY = 0.1
start = time.time()
with Timeout(XDELAY, False):
sleep(XDELAY * 2)
delta = time.time() - start
self.assertTimeWithinRange(delta, 0, XDELAY * 2)
# passing None as seconds disables the timer
with Timeout(None):
sleep(DELAY)
sleep(DELAY)
def test_ref(self):
err = Error()
err_ref = weakref.ref(err)
with Timeout(DELAY * 2, err):
sleep(DELAY)
del err
gc.collect()
self.assertFalse(err_ref(), err_ref)
@flaky.reraises_flaky_race_condition()
def test_nested_timeout(self):
with Timeout(DELAY, False):
with Timeout(DELAY * 10, False):
sleep(DELAY * 3 * 20)
raise AssertionError('should not get there')
with Timeout(DELAY) as t1:
with Timeout(DELAY * 20) as t2:
with self.assertRaises(Timeout) as exc:
sleep(DELAY * 30)
self.assertIs(exc.exception, t1)
self.assertFalse(t1.pending, t1)
self.assertTrue(t2.pending, t2)
self.assertFalse(t2.pending)
with Timeout(DELAY * 20) as t1:
with Timeout(DELAY) as t2:
with self.assertRaises(Timeout) as exc:
sleep(DELAY * 30)
self.assertIs(exc.exception, t2)
self.assertTrue(t1.pending, t1)
self.assertFalse(t2.pending, t2)
self.assertFalse(t1.pending)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,32 @@
from __future__ import print_function
import pickle
import gevent.testing as greentest
try:
from gevent.resolver.cares import ares_host_result
except ImportError: # pragma: no cover
ares_host_result = None
@greentest.skipIf(ares_host_result is None,
"Must be able to import ares")
class TestPickle(greentest.TestCase):
# Issue 104: ares.ares_host_result unpickleable
def _test(self, protocol):
r = ares_host_result('family', ('arg1', 'arg2', ))
dumped = pickle.dumps(r, protocol)
loaded = pickle.loads(dumped)
self.assertEqual(r, loaded)
# pylint:disable=no-member
self.assertEqual(r.family, loaded.family)
for i in range(0, pickle.HIGHEST_PROTOCOL):
def make_test(j):
return lambda self: self._test(j)
setattr(TestPickle, 'test' + str(i), make_test(i))
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,44 @@
from __future__ import print_function
import unittest
import gevent
try:
from gevent.resolver.ares import Resolver
except ImportError as ex:
Resolver = None
from gevent import socket
import gevent.testing as greentest
from gevent.testing.sockets import udp_listener
@unittest.skipIf(
Resolver is None,
"Needs ares resolver"
)
class TestTimeout(greentest.TestCase):
__timeout__ = 30
def test(self):
listener = self._close_on_teardown(udp_listener())
address = listener.getsockname()
def reader():
while True:
listener.recvfrom(10000)
greader = gevent.spawn(reader)
self._close_on_teardown(greader.kill)
r = Resolver(servers=[address[0]], timeout=0.001, tries=1,
udp_port=address[-1])
self._close_on_teardown(r)
with self.assertRaisesRegex(socket.herror, "ARES_ETIMEOUT"):
r.gethostbyname('www.google.com')
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,211 @@
import os
import tempfile
import gevent
import gevent.testing as greentest
from gevent import backdoor
from gevent import socket
from gevent.testing.params import DEFAULT_BIND_ADDR_TUPLE
from gevent.testing.params import DEFAULT_CONNECT
def read_until(conn, postfix):
read = b''
assert isinstance(postfix, bytes)
while not read.endswith(postfix):
result = conn.recv(1)
if not result:
raise AssertionError('Connection ended before %r. Data read:\n%r' % (postfix, read))
read += result
return read if isinstance(read, str) else read.decode('utf-8')
def readline(conn):
with conn.makefile() as f:
return f.readline()
class WorkerGreenlet(gevent.Greenlet):
spawning_stack_limit = 2
class SocketWithBanner(socket.socket):
__slots__ = ('banner',)
def __init__(self, *args, **kwargs):
self.banner = None
super(SocketWithBanner, self).__init__(*args, **kwargs)
def __enter__(self):
return socket.socket.__enter__(self)
def __exit__(self, t, v, tb):
return socket.socket.__exit__(self, t, v, tb)
@greentest.skipOnAppVeyor(
"With the update to libev 4.31 and potentially closing sockets in the background, "
"alternate tests started hanging on appveyor. Something like .E.E.E. "
"See https://ci.appveyor.com/project/denik/gevent/build/job/n9fynkoyt2bvk8b5 "
"It's not clear why, but presumably a socket isn't getting closed and a watcher is tied "
"to the wrong file descriptor. I haven't been able to reproduce. If it were a systemic "
"problem I'd expect to see more failures, so it is probably specific to resource management "
"in this test."
)
class Test(greentest.TestCase):
__timeout__ = 10
def tearDown(self):
gevent.sleep() # let spawned greenlets die
super(Test, self).tearDown()
def _server_listen_argument(self):
return DEFAULT_BIND_ADDR_TUPLE
def _make_and_start_server(self, *args, **kwargs):
server = backdoor.BackdoorServer(self._server_listen_argument(), *args, **kwargs)
server.start()
return server
def _connection_arguments(self, server):
return ((DEFAULT_CONNECT, server.server_port),)
def _socket_arguments(self):
return ()
def _create_connection(self, server):
conn = SocketWithBanner(*self._socket_arguments())
try:
conn.connect(*self._connection_arguments(server)) # pylint:disable=not-callable
banner = self._wait_for_prompt(conn)
except:
conn.close()
raise
conn.banner = banner
return conn
def _wait_for_prompt(self, conn):
return read_until(conn, b'>>> ')
def _close(self, conn, cmd=b'quit()\r\n)'):
conn.sendall(cmd)
line = readline(conn)
self.assertEqual(line, '')
conn.close()
@greentest.skipOnMacOnCI(
"Sometimes fails to get the right answers; "
"https://travis-ci.org/github/gevent/gevent/jobs/692184822"
)
def test_multi(self):
with self._make_and_start_server() as server:
def connect():
with self._create_connection(server) as conn:
conn.sendall(b'2+2\r\n')
line = readline(conn)
self.assertEqual(line.strip(), '4', repr(line))
self._close(conn)
jobs = [WorkerGreenlet.spawn(connect) for _ in range(10)]
try:
done = gevent.joinall(jobs, raise_error=True)
finally:
gevent.joinall(jobs, raise_error=False)
self.assertEqual(len(done), len(jobs), done)
def test_quit(self):
with self._make_and_start_server() as server:
with self._create_connection(server) as conn:
self._close(conn)
def test_sys_exit(self):
with self._make_and_start_server() as server:
with self._create_connection(server) as conn:
self._close(conn, b'import sys; sys.exit(0)\r\n')
def test_banner(self):
expected_banner = "Welcome stranger!" # native string
with self._make_and_start_server(banner=expected_banner) as server:
with self._create_connection(server) as conn:
banner = conn.banner
self._close(conn)
self.assertEqual(banner[:len(expected_banner)], expected_banner, banner)
def test_builtins(self):
with self._make_and_start_server() as server:
with self._create_connection(server) as conn:
conn.sendall(b'locals()["__builtins__"]\r\n')
response = read_until(conn, b'>>> ')
self._close(conn)
self.assertLess(
len(response), 300,
msg="locals() unusable: %s..." % response)
def test_switch_exc(self):
from gevent.queue import Empty
from gevent.queue import Queue
def bad():
q = Queue()
print('switching out, then throwing in')
try:
q.get(block=True, timeout=0.1)
except Empty:
print("Got Empty")
print('switching out')
gevent.sleep(0.1)
print('switched in')
with self._make_and_start_server(locals={'bad': bad}) as server:
with self._create_connection(server) as conn:
conn.sendall(b'bad()\r\n')
response = self._wait_for_prompt(conn)
self._close(conn)
response = response.replace('\r\n', '\n')
self.assertEqual(
'switching out, then throwing in\nGot Empty\nswitching out\nswitched in\n>>> ',
response)
@greentest.skipUnless(
hasattr(socket, 'AF_UNIX'),
"Needs AF_UNIX support"
)
class TestUnixSocket(Test):
def setUp(self):
super().setUp()
# Create a temporary file for the Unix socket
self.unix_socket_path = tempfile.mktemp()
def tearDown(self):
# Clean up the Unix socket file
if os.path.exists(self.unix_socket_path):
os.unlink(self.unix_socket_path)
super().tearDown()
def _server_listen_argument(self):
# Use the Unix socket for the BackdoorServer
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
listener.bind(self.unix_socket_path)
listener.listen(socket.SOMAXCONN)
except Exception as ex:
listener.close()
raise RuntimeError(f"Failed to bind Unix socket at {self.unix_socket_path}: {ex}")
return listener
def _connection_arguments(self, server):
return (self.unix_socket_path,)
def _socket_arguments(self):
return (socket.AF_UNIX, socket.SOCK_STREAM)
if __name__ == '__main__':
greentest.main() # pragma: testrunner-no-combine
@@ -0,0 +1,114 @@
from __future__ import print_function
import os
import unittest
import gevent
from gevent import core
from gevent.hub import Hub
from gevent.testing import sysinfo
@unittest.skipUnless(
getattr(core, 'LIBEV_EMBED', False),
"Needs embedded libev. "
"hub.loop.fileno is only defined when "
"we embed libev for some reason. "
"Choosing specific backends is also only supported by libev "
"(not libuv), and besides, libuv has a nasty tendency to "
"abort() the process if its FD gets closed. "
)
class Test(unittest.TestCase):
# NOTE that we extend unittest.TestCase, not greentest.TestCase
# Extending the later causes the wrong hub to get used.
BACKENDS_THAT_SUCCEED_WHEN_FD_CLOSED = (
'kqueue',
'epoll',
'linux_aio',
'linux_iouring',
)
BACKENDS_THAT_WILL_FAIL_TO_CREATE_AT_RUNTIME = (
# This fails on the Fedora Rawhide 33 image. It's not clear
# why; needs investigated.
'linux_iouring',
) if not sysinfo.libev_supports_linux_iouring() else (
# Even on systems that ought to support it, according to the
# version number, it often fails to actually work. See the
# manylinux_2_28_x86_64 image as-of 2024-10-03; it seems that
# 6.8.0-1014-azure, as used in the Ubuntu 22.04 Github Actions
# runner works fine, though. So it's not clear what we can do
# other than skip it. It's possible this has to do with
# running the manylinux images in docker? " Docker also
# consequently disabled io_uring from their default seccomp
# profile."
)
BACKENDS_TO_SKIP = (
'linux_iouring',
)
BACKENDS_THAT_WILL_FAIL_TO_CREATE_AT_RUNTIME += (
# This can be compiled on any (?) version of
# linux, but there's a runtime check that you're
# running at least kernel 4.19, so we can fail to create
# the hub. When we updated to libev 4.31 from 4.25, Travis Ci
# was still on kernel 1.15 (Ubuntu 16.04).
'linux_aio',
) if not sysinfo.libev_supports_linux_aio() else (
)
def _check_backend(self, backend):
hub = Hub(backend, default=False)
try:
self.assertEqual(hub.loop.backend, backend)
gevent.sleep(0.001)
fileno = hub.loop.fileno()
if fileno is None:
return # nothing to close, test implicitly passes.
os.close(fileno)
if backend in self.BACKENDS_THAT_SUCCEED_WHEN_FD_CLOSED:
gevent.sleep(0.001)
else:
with self.assertRaisesRegex(SystemError, "(libev)"):
gevent.sleep(0.001)
hub.destroy()
self.assertIn('destroyed', repr(hub))
finally:
if hub.loop is not None:
hub.destroy()
@classmethod
def _make_test(cls, count, backend): # pylint:disable=no-self-argument
if backend in cls.BACKENDS_TO_SKIP:
def test(self):
self.skipTest('Expecting %s to fail' % (backend, ))
elif backend in cls.BACKENDS_THAT_WILL_FAIL_TO_CREATE_AT_RUNTIME:
def test(self):
with self.assertRaisesRegex(SystemError, 'ev_loop_new'):
Hub(backend, default=False)
else:
def test(self):
self._check_backend(backend)
test.__name__ = 'test_' + backend + '_' + str(count)
return test.__name__, test
@classmethod
def _make_tests(cls):
count = backend = None
for count in range(2):
for backend in core.supported_backends():
name, func = cls._make_test(count, backend)
setattr(cls, name, func)
name = func = None
Test._make_tests()
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,56 @@
from __future__ import absolute_import, print_function, division
import os
import unittest
class TestFSPath(unittest.TestCase):
def setUp(self):
self.__path = None
def __fspath__(self):
if self.__path is not None:
return self.__path
raise AttributeError("Accessing path data")
def _callFUT(self, arg):
from gevent._compat import _fspath
return _fspath(arg)
def test_text(self):
s = u'path'
self.assertIs(s, self._callFUT(s))
def test_bytes(self):
s = b'path'
self.assertIs(s, self._callFUT(s))
def test_None(self):
with self.assertRaises(TypeError):
self._callFUT(None)
def test_working_path(self):
self.__path = u'text'
self.assertIs(self.__path, self._callFUT(self))
self.__path = b'bytes'
self.assertIs(self.__path, self._callFUT(self))
def test_failing_path_AttributeError(self):
self.assertIsNone(self.__path)
with self.assertRaises(AttributeError):
self._callFUT(self)
def test_fspath_non_str(self):
self.__path = object()
with self.assertRaises(TypeError):
self._callFUT(self)
@unittest.skipUnless(hasattr(os, 'fspath'), "Tests native os.fspath")
class TestNativeFSPath(TestFSPath):
def _callFUT(self, arg):
return os.fspath(arg)
if __name__ == '__main__':
unittest.main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,164 @@
from __future__ import absolute_import, print_function, division
# Important: This file should have no dependencies that are part of the
# ``test`` extra, because it is sometimes run for quick checks without those
# installed.
import unittest
import sys
import gevent.testing as greentest
from gevent._config import Loop
available_loops = Loop().get_options()
available_loops.pop('libuv', None)
def not_available(name):
return isinstance(available_loops[name], ImportError)
class WatcherTestMixin(object):
kind = None
def _makeOne(self):
return self.kind(default=False) # pylint:disable=not-callable
def destroyOne(self, loop):
loop.destroy()
def setUp(self):
self.loop = self._makeOne()
self.core = sys.modules[self.kind.__module__]
def tearDown(self):
self.destroyOne(self.loop)
del self.loop
def test_get_version(self):
version = self.core.get_version() # pylint: disable=no-member
self.assertIsInstance(version, str)
self.assertTrue(version)
header_version = self.core.get_header_version() # pylint: disable=no-member
self.assertIsInstance(header_version, str)
self.assertTrue(header_version)
self.assertEqual(version, header_version)
def test_events_conversion(self):
self.assertEqual(self.core._events_to_str(self.core.READ | self.core.WRITE), # pylint: disable=no-member
'READ|WRITE')
def test_EVENTS(self):
self.assertEqual(str(self.core.EVENTS), # pylint: disable=no-member
'gevent.core.EVENTS')
self.assertEqual(repr(self.core.EVENTS), # pylint: disable=no-member
'gevent.core.EVENTS')
def test_io(self):
if greentest.WIN:
# libev raises IOError, libuv raises ValueError
Error = (IOError, ValueError)
else:
Error = ValueError
with self.assertRaises(Error):
self.loop.io(-1, 1)
if hasattr(self.core, 'TIMER'):
# libev
with self.assertRaises(ValueError):
self.loop.io(1, self.core.TIMER) # pylint:disable=no-member
# Test we can set events and io before it's started
if not greentest.WIN:
# We can't do this with arbitrary FDs on windows;
# see libev_vfd.h
io = self.loop.io(1, self.core.READ) # pylint:disable=no-member
io.fd = 2
self.assertEqual(io.fd, 2)
io.events = self.core.WRITE # pylint:disable=no-member
if not hasattr(self.core, 'libuv'):
# libev
# pylint:disable=no-member
self.assertEqual(self.core._events_to_str(io.events), 'WRITE|_IOFDSET')
else:
self.assertEqual(self.core._events_to_str(io.events), # pylint:disable=no-member
'WRITE')
io.start(lambda: None)
io.close()
def test_timer_constructor(self):
with self.assertRaises(ValueError):
self.loop.timer(1, -1)
def test_signal_constructor(self):
with self.assertRaises(ValueError):
self.loop.signal(1000)
class LibevTestMixin(WatcherTestMixin):
def test_flags_conversion(self):
# pylint: disable=no-member
core = self.core
if not greentest.WIN:
self.assertEqual(core.loop(2, default=False).backend_int, 2)
self.assertEqual(core.loop('select', default=False).backend, 'select')
self.assertEqual(core._flags_to_int(None), 0)
self.assertEqual(core._flags_to_int(['kqueue', 'SELECT']), core.BACKEND_KQUEUE | core.BACKEND_SELECT)
self.assertEqual(core._flags_to_list(core.BACKEND_PORT | core.BACKEND_POLL), ['port', 'poll'])
self.assertRaises(ValueError, core.loop, ['port', 'blabla'])
self.assertRaises(TypeError, core.loop, object())
@unittest.skipIf(not_available('libev-cext'), "Needs libev-cext")
class TestLibevCext(LibevTestMixin, unittest.TestCase):
kind = available_loops['libev-cext']
@unittest.skipIf(not_available('libev-cffi'), "Needs libev-cffi")
class TestLibevCffi(LibevTestMixin, unittest.TestCase):
kind = available_loops['libev-cffi']
@unittest.skipIf(not_available('libuv-cffi'), "Needs libuv-cffi")
class TestLibuvCffi(WatcherTestMixin, unittest.TestCase):
kind = available_loops['libuv-cffi']
@greentest.skipOnLibev("libuv-specific")
@greentest.skipOnWindows("Destroying the loop somehow fails")
def test_io_multiplex_events(self):
# pylint:disable=no-member
import socket
sock = socket.socket()
fd = sock.fileno()
core = self.core
read = self.loop.io(fd, core.READ)
write = self.loop.io(fd, core.WRITE)
try:
real_watcher = read._watcher_ref
read.start(lambda: None)
self.assertEqual(real_watcher.events, core.READ)
write.start(lambda: None)
self.assertEqual(real_watcher.events, core.READ | core.WRITE)
write.stop()
self.assertEqual(real_watcher.events, core.READ)
write.start(lambda: None)
self.assertEqual(real_watcher.events, core.READ | core.WRITE)
read.stop()
self.assertEqual(real_watcher.events, core.WRITE)
write.stop()
self.assertEqual(real_watcher.events, 0)
finally:
read.close()
write.close()
sock.close()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,31 @@
from __future__ import print_function
import gevent
import gevent.core
import time
try:
import thread
except ImportError:
import _thread as thread
from gevent import testing as greentest
class Test(greentest.TestCase):
def test(self):
hub = gevent.get_hub()
watcher = hub.loop.async_()
# BWC for <3.7: This should still be an attribute
assert hasattr(hub.loop, 'async')
gevent.spawn_later(0.1, thread.start_new_thread, watcher.send, ())
start = time.time()
with gevent.Timeout(1.0): # Large timeout for appveyor
hub.wait(watcher)
print('Watcher %r reacted after %.6f seconds' % (watcher, time.time() - start - 0.1))
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,32 @@
import gevent
from gevent.hub import get_hub
from gevent import testing as greentest
class Test(greentest.TestCase):
def test(self):
loop = get_hub().loop
called = []
def f():
called.append(1)
x = loop.run_callback(f)
assert x, x
gevent.sleep(0)
assert called == [1], called
assert not x, (x, bool(x))
x = loop.run_callback(f)
assert x, x
x.stop()
assert not x, x
gevent.sleep(0)
assert called == [1], called
assert not x, x
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,84 @@
from __future__ import print_function
from gevent import monkey
monkey.patch_all()
import os
import unittest
import multiprocessing
import gevent
hub = gevent.get_hub()
pid = os.getpid()
newpid = None
def on_fork():
global newpid
newpid = os.getpid()
fork_watcher = hub.loop.fork(ref=False)
fork_watcher.start(on_fork)
def in_child(q):
# libev only calls fork callbacks at the beginning of
# the loop; we use callbacks extensively so it takes *two*
# calls to sleep (with a timer) to actually get wrapped
# around to the beginning of the loop.
gevent.sleep(0.001)
gevent.sleep(0.001)
q.put(newpid)
class Test(unittest.TestCase):
def test(self):
self.assertEqual(hub.threadpool.size, 0)
# Use a thread to make us multi-threaded
hub.threadpool.apply(lambda: None)
self.assertEqual(hub.threadpool.size, 1)
# Not all platforms use fork by default, so we want to force it,
# where possible. The test is still useful even if we can't
# fork though.
try:
fork_ctx = multiprocessing.get_context('fork')
except (AttributeError, ValueError):
# ValueError if fork isn't supported.
# AttributeError on Python 2, which doesn't have get_context
fork_ctx = multiprocessing
# If the Queue is global, q.get() hangs on Windows; must pass as
# an argument.
q = fork_ctx.Queue()
p = fork_ctx.Process(target=in_child, args=(q,))
p.start()
p.join()
p_val = q.get()
self.assertIsNone(
newpid,
"The fork watcher ran in the parent for some reason."
)
self.assertIsNotNone(
p_val,
"The child process returned nothing, meaning the fork watcher didn't run in the child."
)
self.assertNotEqual(p_val, pid)
assert p_val != pid
if __name__ == '__main__':
# Must call for Windows to fork properly; the fork can't be in the top-level
multiprocessing.freeze_support()
# fork watchers weren't firing in multi-threading processes.
# This test is designed to prove that they are.
# However, it fails on Windows: The fork watcher never runs!
# This makes perfect sense: on Windows, our patches to os.fork()
# that call gevent.hub.reinit() don't get used; os.fork doesn't
# exist and multiprocessing.Process uses the windows-specific _subprocess.CreateProcess()
# to create a whole new process that has no relation to the current process;
# that process then calls multiprocessing.forking.main() to do its work.
# Since no state is shared, a fork watcher cannot exist in that process.
unittest.main()
@@ -0,0 +1,22 @@
from __future__ import print_function
import sys
from gevent import core
from gevent import signal_handler as signal
loop = core.loop(default=False)
signal = signal(2, sys.stderr.write, 'INTERRUPT!')
print('must exit immediately...')
loop.run() # must exit immediately
print('...and once more...')
loop.run() # repeating does not fail
print('..done')
print('must exit after 0.5 seconds.')
timer = loop.timer(0.5)
timer.start(lambda: None)
loop.run()
timer.close()
loop.destroy()
del loop
@@ -0,0 +1,118 @@
from __future__ import print_function
import os
import tempfile
import time
import gevent
import gevent.core
import gevent.testing as greentest
import gevent.testing.flaky
#pylint: disable=protected-access
DELAY = 0.5
WIN = greentest.WIN
LIBUV = greentest.LIBUV
class TestCoreStat(greentest.TestCase):
__timeout__ = greentest.LARGE_TIMEOUT
def setUp(self):
super(TestCoreStat, self).setUp()
fd, path = tempfile.mkstemp(suffix='.gevent_test_core_stat')
os.close(fd)
self.temp_path = path
self.hub = gevent.get_hub()
# If we don't specify an interval, we default to zero.
# libev interprets that as meaning to use its default interval,
# which is about 5 seconds. If we go below it's minimum check
# threshold, it bumps it up to the minimum.
self.watcher = self.hub.loop.stat(self.temp_path, interval=-1)
def tearDown(self):
self.watcher.close()
if os.path.exists(self.temp_path):
os.unlink(self.temp_path)
super(TestCoreStat, self).tearDown()
def _write(self):
with open(self.temp_path, 'wb', buffering=0) as f:
f.write(b'x')
def _check_attr(self, name, none):
# Deals with the complex behaviour of the 'attr' and 'prev'
# attributes on Windows. This codifies it, rather than simply letting
# the test fail, so we know exactly when and what changes it.
try:
x = getattr(self.watcher, name)
except ImportError:
if WIN:
# the 'posix' module is not available
pass
else:
raise
else:
if WIN and not LIBUV:
# The ImportError is only raised for the first time;
# after that, the attribute starts returning None
self.assertIsNone(x, "Only None is supported on Windows")
if none:
self.assertIsNone(x, name)
else:
self.assertIsNotNone(x, name)
def _wait_on_greenlet(self, func, *greenlet_args):
start = time.time()
self.hub.loop.update_now()
greenlet = gevent.spawn_later(DELAY, func, *greenlet_args)
with gevent.Timeout(5 + DELAY + 0.5):
self.hub.wait(self.watcher)
now = time.time()
self.assertGreaterEqual(now, start, "Time must move forward")
wait_duration = now - start
reaction = wait_duration - DELAY
if reaction <= 0.0:
# Sigh. This is especially true on PyPy on Windows
raise gevent.testing.flaky.FlakyTestRaceCondition(
"Bad timer resolution (on Windows?), test is useless. Start %s, now %s" % (start, now))
self.assertGreaterEqual(
reaction, 0.0,
'Watcher %s reacted too early: %.3fs' % (self.watcher, reaction))
greenlet.join()
def test_watcher_basics(self):
watcher = self.watcher
filename = self.temp_path
self.assertEqual(watcher.path, filename)
filenames = filename if isinstance(filename, bytes) else filename.encode('ascii')
self.assertEqual(watcher._paths, filenames)
self.assertEqual(watcher.interval, -1)
def test_write(self):
self._wait_on_greenlet(self._write)
self._check_attr('attr', False)
self._check_attr('prev', False)
# The watcher interval changed after it started; -1 is illegal
self.assertNotEqual(self.watcher.interval, -1)
def test_unlink(self):
self._wait_on_greenlet(os.unlink, self.temp_path)
self._check_attr('attr', True)
self._check_attr('prev', False)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,157 @@
from __future__ import print_function
from gevent import config
import gevent.testing as greentest
from gevent.testing import TestCase
from gevent.testing import LARGE_TIMEOUT
from gevent.testing.sysinfo import CFFI_BACKEND
from gevent.testing.flaky import reraises_flaky_timeout
class Test(TestCase):
__timeout__ = LARGE_TIMEOUT
repeat = 0
timer_duration = 0.001
def setUp(self):
super(Test, self).setUp()
self.called = []
self.loop = config.loop(default=False)
self.timer = self.loop.timer(self.timer_duration, repeat=self.repeat)
assert not self.loop.default
def cleanup(self):
# cleanup instead of tearDown to cooperate well with
# leakcheck.py
self.timer.close()
# cycle the loop so libuv close callbacks fire
self.loop.run()
self.loop.destroy()
self.loop = None
self.timer = None
def f(self, x=None):
self.called.append(1)
if x is not None:
x.stop()
def assertTimerInKeepalive(self):
if CFFI_BACKEND:
self.assertIn(self.timer, self.loop._keepaliveset)
def assertTimerNotInKeepalive(self):
if CFFI_BACKEND:
self.assertNotIn(self.timer, self.loop._keepaliveset)
def test_main(self):
loop = self.loop
x = self.timer
x.start(self.f)
self.assertTimerInKeepalive()
self.assertTrue(x.active, x)
with self.assertRaises((AttributeError, ValueError)):
x.priority = 1
loop.run()
self.assertEqual(x.pending, 0)
self.assertEqual(self.called, [1])
self.assertIsNone(x.callback)
self.assertIsNone(x.args)
if x.priority is not None:
self.assertEqual(x.priority, 0)
x.priority = 1
self.assertEqual(x.priority, 1)
x.stop()
self.assertTimerNotInKeepalive()
class TestAgain(Test):
repeat = 1
def test_main(self):
# Again works for a new timer
x = self.timer
x.again(self.f, x)
self.assertTimerInKeepalive()
self.assertEqual(x.args, (x,))
# XXX: On libev, this takes 1 second. On libuv,
# it takes the expected time.
self.loop.run()
self.assertEqual(self.called, [1])
x.stop()
self.assertTimerNotInKeepalive()
class TestTimerResolution(Test):
# On CI, with *all* backends, sometimes we get timer values of
# 0.02 or higher.
@reraises_flaky_timeout(AssertionError)
def test_resolution(self): # pylint:disable=too-many-locals
# Make sure that having an active IO watcher
# doesn't badly throw off our timer resolution.
# (This was a specific problem with libuv)
# https://github.com/gevent/gevent/pull/1194
from gevent._compat import perf_counter
import socket
s = socket.socket()
self._close_on_teardown(s)
fd = s.fileno()
ran_at_least_once = False
fired_at = []
def timer_counter():
fired_at.append(perf_counter())
loop = self.loop
timer_multiplier = 11
max_time = self.timer_duration * timer_multiplier
assert max_time < 0.3
for _ in range(150):
# in libuv, our signal timer fires every 300ms; depending on
# when this runs, we could artificially get a better
# resolution than we expect. Run it multiple times to be more sure.
io = loop.io(fd, 1)
io.start(lambda events=None: None)
now = perf_counter()
del fired_at[:]
timer = self.timer
timer.start(timer_counter)
loop.run(once=True)
io.stop()
io.close()
timer.stop()
if fired_at:
ran_at_least_once = True
self.assertEqual(1, len(fired_at))
self.assertTimeWithinRange(fired_at[0] - now,
0,
max_time)
if not greentest.RUNNING_ON_CI:
# Hmm, this always fires locally on mocOS but
# not an Travis?
self.assertTrue(ran_at_least_once)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,124 @@
from __future__ import absolute_import, print_function
import gevent.testing as greentest
from gevent import config
from gevent.testing.sysinfo import CFFI_BACKEND
from gevent.core import READ # pylint:disable=no-name-in-module
from gevent.core import WRITE # pylint:disable=no-name-in-module
class Test(greentest.TestCase):
__timeout__ = None
def setUp(self):
super(Test, self).setUp()
self.loop = config.loop(default=False)
self.timer = self.loop.timer(0.01)
def tearDown(self):
if self.timer is not None:
self.timer.close()
if self.loop is not None:
self.loop.destroy()
self.loop = self.timer = None
super(Test, self).tearDown()
def test_non_callable_to_start(self):
# test that cannot pass non-callable thing to start()
self.assertRaises(TypeError, self.timer.start, None)
self.assertRaises(TypeError, self.timer.start, 5)
def test_non_callable_after_start(self):
# test that cannot set 'callback' to non-callable thing later either
lst = []
timer = self.timer
timer.start(lst.append)
with self.assertRaises(TypeError):
timer.callback = False
with self.assertRaises(TypeError):
timer.callback = 5
def test_args_can_be_changed_after_start(self):
lst = []
timer = self.timer
self.timer.start(lst.append)
self.assertEqual(timer.args, ())
timer.args = (1, 2, 3)
self.assertEqual(timer.args, (1, 2, 3))
# Only tuple can be args
with self.assertRaises(TypeError):
timer.args = 5
with self.assertRaises(TypeError):
timer.args = [4, 5]
self.assertEqual(timer.args, (1, 2, 3))
# None also works, means empty tuple
# XXX why?
timer.args = None
self.assertEqual(timer.args, None)
def test_run(self):
loop = self.loop
lst = []
self.timer.start(lambda *args: lst.append(args))
loop.run()
loop.update_now()
self.assertEqual(lst, [()])
# Even if we lose all references to it, the ref in the callback
# keeps it alive
self.timer.start(reset, self.timer, lst)
self.timer = None
loop.run()
self.assertEqual(lst, [(), 25])
def test_invalid_fd(self):
loop = self.loop
# Negative case caught everywhere. ValueError
# on POSIX, OSError on Windows Py3, IOError on Windows Py2
with self.assertRaises((ValueError, OSError, IOError)):
loop.io(-1, READ)
@greentest.skipOnWindows("Stdout can't be watched on Win32")
def test_reuse_io(self):
loop = self.loop
# Watchers aren't reused once all outstanding
# refs go away BUT THEY MUST BE CLOSED
tty_watcher = loop.io(1, WRITE)
watcher_handle = tty_watcher._watcher if CFFI_BACKEND else tty_watcher
tty_watcher.close()
del tty_watcher
# XXX: Note there is a cycle in the CFFI code
# from watcher_handle._handle -> watcher_handle.
# So it doesn't go away until a GC runs.
import gc
gc.collect()
tty_watcher = loop.io(1, WRITE)
self.assertIsNot(tty_watcher._watcher if CFFI_BACKEND else tty_watcher, watcher_handle)
tty_watcher.close()
def reset(watcher, lst):
watcher.args = None
watcher.callback = lambda: None
lst.append(25)
watcher.close()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,52 @@
from __future__ import absolute_import, print_function
import gevent
import unittest
class TestDestroyHub(unittest.TestCase):
def test_destroy_hub(self):
# Loop of initial Hub is default loop.
hub = gevent.get_hub()
self.assertTrue(hub.loop.default)
# Save `gevent.core.loop` object for later comparison.
initloop = hub.loop
# Increase test complexity via threadpool creation.
# Implicitly creates fork watcher connected to the current event loop.
tp = hub.threadpool
self.assertIsNotNone(tp)
# Destroy hub. Does not destroy libev default loop if not explicitly told to.
hub.destroy()
# Create new hub. Must re-use existing libev default loop.
hub = gevent.get_hub()
self.assertTrue(hub.loop.default)
# Ensure that loop object is identical to the initial one.
self.assertIs(hub.loop, initloop)
# Destroy hub including default loop.
hub.destroy(destroy_loop=True)
# Create new hub and explicitly request creation of a new default loop.
# (using default=True, but that's no longer possible.)
hub = gevent.get_hub()
self.assertTrue(hub.loop.default)
# `gevent.core.loop` objects as well as libev loop pointers must differ.
self.assertIsNot(hub.loop, initloop)
self.assertIsNot(hub.loop.ptr, initloop.ptr)
self.assertNotEqual(hub.loop.ptr, initloop.ptr)
# Destroy hub including default loop. The default loop regenerates.
hub.destroy(destroy_loop=True)
hub = gevent.get_hub()
self.assertTrue(hub.loop.default)
hub.destroy()
if __name__ == '__main__':
unittest.main() # pragma: testrunner-no-combine
@@ -0,0 +1,72 @@
from __future__ import print_function
import gevent
import unittest
class TestDestroyDefaultLoop(unittest.TestCase):
def tearDown(self):
self._reset_hub()
super(TestDestroyDefaultLoop, self).tearDown()
def _reset_hub(self):
from gevent._hub_local import set_hub
from gevent._hub_local import set_loop
from gevent._hub_local import get_hub_if_exists
hub = get_hub_if_exists()
if hub is not None:
hub.destroy(destroy_loop=True)
set_hub(None)
set_loop(None)
def test_destroy_gc(self):
# Issue 1098: destroying the default loop
# while using the C extension could crash
# the interpreter when it exits
# Create the hub greenlet. This creates one loop
# object pointing to the default loop.
gevent.get_hub()
# Get a new loop object, but using the default
# C loop
loop = gevent.config.loop(default=True)
self.assertTrue(loop.default)
# Destroy it
loop.destroy()
# It no longer claims to be the default
self.assertFalse(loop.default)
# Delete it
del loop
# Delete the hub. This prompts garbage
# collection of it and its loop object.
# (making this test more repeatable; the exit
# crash only happened when that greenlet object
# was collected at exit time, which was most common
# in CPython 3.5)
self._reset_hub()
def test_destroy_two(self):
# Get two new loop object, but using the default
# C loop
loop1 = gevent.config.loop(default=True)
loop2 = gevent.config.loop(default=True)
self.assertTrue(loop1.default)
self.assertTrue(loop2.default)
# Destroy the first
loop1.destroy()
# It no longer claims to be the default
self.assertFalse(loop1.default)
# Destroy the second. This doesn't crash.
loop2.destroy()
self.assertFalse(loop2.default)
self.assertFalse(loop2.ptr)
self._reset_hub()
self.assertTrue(gevent.get_hub().loop.ptr)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,133 @@
from __future__ import print_function
import doctest
import functools
import os
import re
import sys
import unittest
# Ignore tracebacks: ZeroDivisionError
def myfunction(*_args, **_kwargs):
pass
class RENormalizingOutputChecker(doctest.OutputChecker):
"""
Pattern-normalizing output checker. Inspired by one used in zope.testing.
"""
def __init__(self, patterns):
self.transformers = [functools.partial(re.sub, replacement) for re, replacement in patterns]
def check_output(self, want, got, optionflags):
if got == want:
return True
for transformer in self.transformers:
want = transformer(want)
got = transformer(got)
return doctest.OutputChecker.check_output(self, want, got, optionflags)
FORBIDDEN_MODULES = set()
class Modules(object):
def __init__(self, allowed_modules):
from gevent.testing import walk_modules
self.allowed_modules = allowed_modules
self.modules = set()
for path, module in walk_modules(recursive=True):
self.add_module(module, path)
def add_module(self, name, path):
if self.allowed_modules and name not in self.allowed_modules:
return
if name in FORBIDDEN_MODULES:
return
self.modules.add((name, path))
def __bool__(self):
return bool(self.modules)
__nonzero__ = __bool__
def __iter__(self):
return iter(self.modules)
def main(): # pylint:disable=too-many-locals
cwd = os.getcwd()
# Use pure_python to get the correct module source and docstrings
os.environ['PURE_PYTHON'] = '1'
import gevent
from gevent import socket
from gevent.testing import util
from gevent.testing import sysinfo
if sysinfo.WIN:
FORBIDDEN_MODULES.update({
# Uses commands only found on posix
'gevent.subprocess',
})
try:
allowed_modules = sys.argv[1:]
sys.path.append('.')
globs = {
'myfunction': myfunction,
'gevent': gevent,
'socket': socket,
}
modules = Modules(allowed_modules)
if not modules:
sys.exit('No modules found matching %s' % ' '.join(allowed_modules))
suite = unittest.TestSuite()
checker = RENormalizingOutputChecker((
# Normalize subprocess.py: BSD ls is in the example, gnu ls outputs
# 'cannot access'
(re.compile(
"ls: cannot access 'non_existent_file': No such file or directory"),
"ls: non_existent_file: No such file or directory"),
# Python 3 bytes add a "b".
(re.compile(r'b(".*?")'), r"\1"),
(re.compile(r"b('.*?')"), r"\1"),
))
tests_count = 0
modules_count = 0
for m, path in sorted(modules):
with open(path, 'rb') as f:
contents = f.read()
if re.search(br'^\s*>>> ', contents, re.M):
s = doctest.DocTestSuite(m, extraglobs=globs, checker=checker)
test_count = len(s._tests)
util.log('%s (from %s): %s tests', m, path, test_count)
suite.addTest(s)
modules_count += 1
tests_count += test_count
util.log('Total: %s tests in %s modules', tests_count, modules_count)
# TODO: Pass this off to unittest.main()
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
finally:
os.chdir(cwd)
if __name__ == '__main__':
main()
@@ -0,0 +1,19 @@
import os
import sys
import gevent
import gevent.core
import subprocess
if not sys.argv[1:]:
os.environ['GEVENT_BACKEND'] = 'select'
# (not in Py2) pylint:disable=consider-using-with
popen = subprocess.Popen([sys.executable, __file__, '1'])
assert popen.wait() == 0, popen.poll()
else: # pragma: no cover
hub = gevent.get_hub()
# pylint:disable-next=no-member
if 'select' in gevent.core.supported_backends():
assert hub.loop.backend == 'select', hub.loop.backend
else:
# libuv isn't configurable
assert hub.loop.backend == 'default', hub.loop.backend
@@ -0,0 +1,443 @@
import weakref
import gevent
from gevent.event import Event, AsyncResult
import gevent.testing as greentest
from gevent.testing.timing import AbstractGenericGetTestCase
from gevent.testing.timing import AbstractGenericWaitTestCase
from gevent.testing.timing import SMALL_TICK
from gevent.testing.timing import SMALL_TICK_MAX_ADJ
DELAY = SMALL_TICK + SMALL_TICK_MAX_ADJ
class TestEventWait(AbstractGenericWaitTestCase):
def wait(self, timeout):
Event().wait(timeout=timeout)
def test_cover(self):
str(Event())
class TestGeventWaitOnEvent(AbstractGenericWaitTestCase):
def wait(self, timeout):
gevent.wait([Event()], timeout=timeout)
def test_set_during_wait(self):
# https://github.com/gevent/gevent/issues/771
# broke in the refactoring. we must not add new links
# while we're running the callback
event = Event()
def setter():
event.set()
def waiter():
s = gevent.spawn(setter)
# let the setter set() the event;
# when this method returns we'll be running in the Event._notify_links callback
# (that is, it switched to us)
res = event.wait()
self.assertTrue(res)
self.assertTrue(event.ready())
s.join() # make sure it's dead
# Clear the event. Now we can't wait for the event without
# another set to happen.
event.clear()
self.assertFalse(event.ready())
# Before the bug fix, this would return "immediately" with
# event in the result list, because the _notify_links loop would
# immediately add the waiter and call it
o = gevent.wait((event,), timeout=0.01)
self.assertFalse(event.ready())
self.assertNotIn(event, o)
gevent.spawn(waiter).join()
class TestAsyncResultWait(AbstractGenericWaitTestCase):
def wait(self, timeout):
AsyncResult().wait(timeout=timeout)
class TestWaitAsyncResult(AbstractGenericWaitTestCase):
def wait(self, timeout):
gevent.wait([AsyncResult()], timeout=timeout)
class TestAsyncResultGet(AbstractGenericGetTestCase):
def wait(self, timeout):
AsyncResult().get(timeout=timeout)
class MyException(Exception):
pass
class TestAsyncResult(greentest.TestCase):
def test_link(self):
ar = AsyncResult()
self.assertRaises(TypeError, ar.rawlink, None)
ar.unlink(None) # doesn't raise
ar.unlink(None) # doesn't raise
str(ar) # cover
def test_set_exc(self):
log = []
e = AsyncResult()
self.assertEqual(e.exc_info, ())
self.assertEqual(e.exception, None)
def waiter():
with self.assertRaises(MyException) as exc:
e.get()
log.append(('caught', exc.exception))
gevent.spawn(waiter)
obj = MyException()
e.set_exception(obj)
gevent.sleep(0)
self.assertEqual(log, [('caught', obj)])
def test_set(self):
event1 = AsyncResult()
timer_exc = MyException('interrupted')
# Notice that this test is racy:
# After DELAY, we set the event. We also try to immediately
# raise the exception with a timer of 0 --- but that depends
# on cycling the loop. Hence the fairly large value for DELAY.
g = gevent.spawn_later(DELAY, event1.set, 'hello event1')
self._close_on_teardown(g.kill)
with gevent.Timeout.start_new(0, timer_exc):
with self.assertRaises(MyException) as exc:
event1.get()
self.assertIs(timer_exc, exc.exception)
def test_set_with_timeout(self):
event2 = AsyncResult()
X = object()
result = gevent.with_timeout(DELAY, event2.get, timeout_value=X)
self.assertIs(
result, X,
'Nobody sent anything to event2 yet it received %r' % (result, ))
def test_nonblocking_get(self):
ar = AsyncResult()
self.assertRaises(gevent.Timeout, ar.get, block=False)
self.assertRaises(gevent.Timeout, ar.get_nowait)
class TestAsyncResultCrossThread(greentest.TestCase):
def _makeOne(self):
return AsyncResult()
def _setOne(self, one):
one.set('from main')
BG_WAIT_DELAY = 60
def _check_pypy_switch(self):
# On PyPy 7.3.3, switching to the main greenlet of a thread from a
# different thread silently does nothing. We can't detect the cross-thread
# switch, and so this test breaks
# https://foss.heptapod.net/pypy/pypy/-/issues/3381
if greentest.PYPY:
import sys
if sys.pypy_version_info[:3] <= (7, 3, 3): # pylint:disable=no-member
self.skipTest("PyPy bug: https://foss.heptapod.net/pypy/pypy/-/issues/3381")
@greentest.ignores_leakcheck
def test_cross_thread_use(self, timed_wait=False, wait_in_bg=False):
# Issue 1739.
# AsyncResult has *never* been thread safe, and using it from one
# thread to another is not safe. However, in some very careful use cases
# that can actually work.
#
# This test makes sure it doesn't hang in one careful use
# scenario.
self.assertNotMonkeyPatched() # Need real threads, event objects
from threading import Thread as NativeThread
from threading import Event as NativeEvent
if not wait_in_bg:
self._check_pypy_switch()
test = self
class Thread(NativeThread):
def __init__(self):
NativeThread.__init__(self)
self.daemon = True
self.running_event = NativeEvent()
self.finished_event = NativeEvent()
self.async_result = test._makeOne()
self.result = '<never set>'
def run(self):
# Give the loop in this thread something to do
g_event = Event()
def spin():
while not g_event.is_set():
g_event.wait(DELAY * 2)
glet = gevent.spawn(spin)
def work():
self.running_event.set()
# If we use a timed wait(), the bug doesn't manifest.
# This is probably because the loop wakes up to handle the timer,
# and notices the callback.
# See https://github.com/gevent/gevent/issues/1735
if timed_wait:
self.result = self.async_result.wait(test.BG_WAIT_DELAY)
else:
self.result = self.async_result.wait()
if wait_in_bg:
# This results in a separate code path
worker = gevent.spawn(work)
worker.join()
del worker
else:
work()
g_event.set()
glet.join()
del glet
self.finished_event.set()
gevent.get_hub().destroy(destroy_loop=True)
thread = Thread()
thread.start()
try:
thread.running_event.wait()
self._setOne(thread.async_result)
thread.finished_event.wait(DELAY * 5)
finally:
thread.join(DELAY * 15)
self._check_result(thread.result)
def _check_result(self, result):
self.assertEqual(result, 'from main')
def test_cross_thread_use_bg(self):
self.test_cross_thread_use(timed_wait=False, wait_in_bg=True)
def test_cross_thread_use_timed(self):
self.test_cross_thread_use(timed_wait=True, wait_in_bg=False)
def test_cross_thread_use_timed_bg(self):
self.test_cross_thread_use(timed_wait=True, wait_in_bg=True)
@greentest.ignores_leakcheck
def test_cross_thread_use_set_in_bg(self):
self.assertNotMonkeyPatched() # Need real threads, event objects
from threading import Thread as NativeThread
from threading import Event as NativeEvent
self._check_pypy_switch()
test = self
class Thread(NativeThread):
def __init__(self):
NativeThread.__init__(self)
self.daemon = True
self.running_event = NativeEvent()
self.finished_event = NativeEvent()
self.async_result = test._makeOne()
self.result = '<never set>'
def run(self):
self.running_event.set()
test._setOne(self.async_result)
self.finished_event.set()
gevent.get_hub().destroy(destroy_loop=True)
thread = Thread()
glet = None
try:
glet = gevent.spawn(thread.start)
result = thread.async_result.wait(self.BG_WAIT_DELAY)
finally:
thread.join(DELAY * 15)
if glet is not None:
glet.join(DELAY)
self._check_result(result)
@greentest.ignores_leakcheck
def test_cross_thread_use_set_in_bg2(self):
# Do it again to make sure it works multiple times.
self.test_cross_thread_use_set_in_bg()
class TestEventCrossThread(TestAsyncResultCrossThread):
def _makeOne(self):
return Event()
def _setOne(self, one):
one.set()
def _check_result(self, result):
self.assertTrue(result)
class TestAsyncResultAsLinkTarget(greentest.TestCase):
error_fatal = False
def test_set(self):
g = gevent.spawn(lambda: 1)
s1, s2, s3 = AsyncResult(), AsyncResult(), AsyncResult()
g.link(s1)
g.link_value(s2)
g.link_exception(s3)
self.assertEqual(s1.get(), 1)
self.assertEqual(s2.get(), 1)
X = object()
result = gevent.with_timeout(DELAY, s3.get, timeout_value=X)
self.assertIs(result, X)
def test_set_exception(self):
def func():
raise greentest.ExpectedException('TestAsyncResultAsLinkTarget.test_set_exception')
g = gevent.spawn(func)
s1, s2, s3 = AsyncResult(), AsyncResult(), AsyncResult()
g.link(s1)
g.link_value(s2)
g.link_exception(s3)
self.assertRaises(greentest.ExpectedException, s1.get)
X = object()
result = gevent.with_timeout(DELAY, s2.get, timeout_value=X)
self.assertIs(result, X)
self.assertRaises(greentest.ExpectedException, s3.get)
class TestEvent_SetThenClear(greentest.TestCase):
N = 1
def test(self):
e = Event()
waiters = [gevent.spawn(e.wait) for i in range(self.N)]
gevent.sleep(0.001)
e.set()
e.clear()
for greenlet in waiters:
greenlet.join()
class TestEvent_SetThenClear100(TestEvent_SetThenClear):
N = 100
class TestEvent_SetThenClear1000(TestEvent_SetThenClear):
N = 1000
class TestWait(greentest.TestCase):
N = 5
count = None
timeout = 1
period = timeout / 100.0
def _sender(self, events, asyncs):
while events or asyncs:
gevent.sleep(self.period)
if events:
events.pop().set()
gevent.sleep(self.period)
if asyncs:
asyncs.pop().set()
@greentest.skipOnAppVeyor("Not all results have arrived sometimes due to timer issues")
def test(self):
events = [Event() for _ in range(self.N)]
asyncs = [AsyncResult() for _ in range(self.N)]
max_len = len(events) + len(asyncs)
sender = gevent.spawn(self._sender, events, asyncs)
results = gevent.wait(events + asyncs, count=self.count, timeout=self.timeout)
if self.timeout is None:
expected_len = max_len
else:
expected_len = min(max_len, self.timeout / self.period)
if self.count is None:
self.assertTrue(sender.ready(), sender)
else:
expected_len = min(self.count, expected_len)
self.assertFalse(sender.ready(), sender)
sender.kill()
self.assertEqual(expected_len, len(results), (expected_len, len(results), results))
class TestWait_notimeout(TestWait):
timeout = None
class TestWait_count1(TestWait):
count = 1
class TestWait_count2(TestWait):
count = 2
class TestEventBasics(greentest.TestCase):
def test_weakref(self):
# Event objects should allow weakrefs
e = Event()
r = weakref.ref(e)
self.assertIs(e, r())
del e
del r
def test_wait_while_notifying(self):
# If someone calls wait() on an Event that is
# ready, and notifying other waiters, that new
# waiter still runs at the end, but this does not
# require a trip around the event loop.
# See https://github.com/gevent/gevent/issues/1520
event = Event()
results = []
def wait_then_append(arg):
event.wait()
results.append(arg)
gevent.spawn(wait_then_append, 1)
gevent.spawn(wait_then_append, 2)
gevent.idle()
self.assertEqual(2, event.linkcount())
check = gevent.get_hub().loop.check()
check.start(results.append, 4)
event.set()
wait_then_append(3)
self.assertEqual(results, [1, 2, 3])
# Note that the check event DID NOT run.
check.stop()
check.close()
def test_gevent_wait_twice_when_already_set(self):
event = Event()
event.set()
# First one works fine.
result = gevent.wait([event])
self.assertEqual(result, [event])
# Second one used to fail with an AssertionError,
# now it passes
result = gevent.wait([event])
self.assertEqual(result, [event])
del AbstractGenericGetTestCase
del AbstractGenericWaitTestCase
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# Copyright 2018 gevent. See LICENSE.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import unittest
from gevent import events
try:
from zope.interface import verify
except ImportError:
verify = None
try:
from zope import event
except ImportError:
event = None
@unittest.skipIf(verify is None, "Needs zope.interface")
class TestImplements(unittest.TestCase):
def test_event_loop_blocked(self):
verify.verifyClass(events.IEventLoopBlocked, events.EventLoopBlocked)
def test_mem_threshold(self):
verify.verifyClass(events.IMemoryUsageThresholdExceeded,
events.MemoryUsageThresholdExceeded)
verify.verifyObject(events.IMemoryUsageThresholdExceeded,
events.MemoryUsageThresholdExceeded(0, 0, 0))
def test_mem_decreased(self):
verify.verifyClass(events.IMemoryUsageUnderThreshold,
events.MemoryUsageUnderThreshold)
verify.verifyObject(events.IMemoryUsageUnderThreshold,
events.MemoryUsageUnderThreshold(0, 0, 0, 0))
@unittest.skipIf(event is None, "Needs zope.event")
class TestEvents(unittest.TestCase):
def test_is_zope(self):
self.assertIs(events.subscribers, event.subscribers)
self.assertIs(events.notify, event.notify)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,36 @@
from gevent.socket import create_connection, timeout
import gevent.testing as greentest
import gevent
from gevent.testing import util
from gevent.testing import params
class Test(util.TestServer):
example = 'echoserver.py'
def _run_all_tests(self):
def test_client(message):
kwargs = {'buffering': 1, 'mode': 'rb'}
conn = create_connection((params.DEFAULT_LOCAL_HOST_ADDR, 16000))
conn.settimeout(greentest.DEFAULT_XPC_SOCKET_TIMEOUT)
rfile = conn.makefile(**kwargs)
welcome = rfile.readline()
self.assertIn(b'Welcome', welcome)
conn.sendall(message)
received = rfile.read(len(message))
self.assertEqual(received, message)
self.assertRaises(timeout, conn.recv, 1)
rfile.close()
conn.close()
client1 = gevent.spawn(test_client, b'hello\r\n')
client2 = gevent.spawn(test_client, b'world\r\n')
gevent.joinall([client1, client2], raise_error=True)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,67 @@
from __future__ import print_function, absolute_import
from gevent import monkey; monkey.patch_all()
import signal
import socket
from time import sleep
import gevent
from gevent.server import StreamServer
import gevent.testing as greentest
from gevent.testing import util
@greentest.skipOnLibuvOnCIOnPyPy("Timing issues sometimes lead to connection refused")
class Test(util.TestServer):
example = 'portforwarder.py'
# [listen on, forward to]
example_args = ['127.0.0.1:10011', '127.0.0.1:10012']
if greentest.WIN:
from subprocess import CREATE_NEW_PROCESS_GROUP
# Must be in a new process group to use CTRL_C_EVENT, otherwise
# we get killed too
start_kwargs = {'creationflags': CREATE_NEW_PROCESS_GROUP}
def after(self):
if greentest.WIN:
self.assertIsNotNone(self.popen.poll())
else:
self.assertEqual(self.popen.poll(), 0)
def _run_all_tests(self):
log = []
def handle(sock, _address):
while True:
data = sock.recv(1024)
print('got %r' % data)
if not data:
break
log.append(data)
server = StreamServer(self.example_args[1], handle)
server.start()
try:
conn = socket.create_connection(('127.0.0.1', 10011))
conn.sendall(b'msg1')
sleep(0.1)
# On Windows, SIGTERM actually abruptly terminates the process;
# it can't be caught. However, CTRL_C_EVENT results in a KeyboardInterrupt
# being raised, so we can shut down properly.
self.popen.send_signal(getattr(signal, 'CTRL_C_EVENT', signal.SIGTERM))
sleep(0.1)
conn.sendall(b'msg2')
conn.close()
with gevent.Timeout(2.1):
self.popen.wait()
finally:
server.close()
self.assertEqual([b'msg1', b'msg2'], log)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,35 @@
from gevent import monkey
monkey.patch_all()
from gevent.server import DatagramServer
from gevent.testing import util
from gevent.testing import main
class Test_udp_client(util.TestServer):
start_kwargs = {'timeout': 10}
example = 'udp_client.py'
example_args = ['Test_udp_client']
def test(self):
log = []
def handle(message, address):
log.append(message)
server.sendto(b'reply-from-server', address)
server = DatagramServer('127.0.0.1:9001', handle)
server.start()
try:
self.run_example()
finally:
server.close()
self.assertEqual(log, [b'Test_udp_client'])
if __name__ == '__main__':
# Running this following test__example_portforwarder on Appveyor
# doesn't work in the same process for some reason.
main() # pragma: testrunner-no-combine
@@ -0,0 +1,22 @@
import socket
from gevent.testing import util
from gevent.testing import main
class Test(util.TestServer):
example = 'udp_server.py'
def _run_all_tests(self):
sock = socket.socket(type=socket.SOCK_DGRAM)
try:
sock.connect(('127.0.0.1', 9000))
sock.send(b'Test udp_server')
data, _address = sock.recvfrom(8192)
self.assertEqual(data, b'Received 15 bytes')
finally:
sock.close()
if __name__ == '__main__':
main()
@@ -0,0 +1,29 @@
from unittest import SkipTest
import gevent.testing as greentest
from . import test__example_wsgiserver
@greentest.skipOnCI("Timing issues sometimes lead to a connection refused")
@greentest.skipWithoutExternalNetwork("Tries to reach google.com")
class Test_webproxy(test__example_wsgiserver.Test_wsgiserver):
example = 'webproxy.py'
def _run_all_tests(self):
status, data = self.read('/')
self.assertEqual(status, '200 OK')
self.assertIn(b"gevent example", data)
status, data = self.read('/http://www.google.com')
self.assertEqual(status, '200 OK')
self.assertIn(b'google', data.lower())
def test_a_blocking_client(self):
# Not applicable
raise SkipTest("Not applicable")
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,94 @@
import sys
try:
from urllib import request as urllib2
except ImportError:
import urllib2
import socket
import ssl
import gevent.testing as greentest
from gevent.testing import DEFAULT_XPC_SOCKET_TIMEOUT
from gevent.testing import util
from gevent.testing import params
@greentest.skipOnCI("Timing issues sometimes lead to a connection refused")
class Test_wsgiserver(util.TestServer):
example = 'wsgiserver.py'
URL = 'http://%s:8088' % (params.DEFAULT_LOCAL_HOST_ADDR,)
PORT = 8088
not_found_message = b'<h1>Not Found</h1>'
ssl_ctx = None
_use_ssl = False
def read(self, path='/'):
url = self.URL + path
try:
kwargs = {}
if self.ssl_ctx is not None:
kwargs = {'context': self.ssl_ctx}
response = urllib2.urlopen(url, None,
DEFAULT_XPC_SOCKET_TIMEOUT,
**kwargs)
except urllib2.HTTPError:
response = sys.exc_info()[1]
result = '%s %s' % (response.code, response.msg), response.read()
# XXX: It looks like under PyPy this isn't directly closing the socket
# when SSL is in use. It takes a GC cycle to make that true.
response.close()
return result
def _test_hello(self):
status, data = self.read('/')
self.assertEqual(status, '200 OK')
self.assertEqual(data, b"<b>hello world</b>")
def _test_not_found(self):
status, data = self.read('/xxx')
self.assertEqual(status, '404 Not Found')
self.assertEqual(data, self.not_found_message)
def _do_test_a_blocking_client(self):
# We spawn this in a separate server because if it's broken
# the whole server hangs
with self.running_server():
# First, make sure we can talk to it.
self._test_hello()
# Now create a connection and only partway finish
# the transaction
sock = socket.create_connection((params.DEFAULT_LOCAL_HOST_ADDR, self.PORT))
ssl_sock = None
if self._use_ssl:
context = ssl.SSLContext()
ssl_sock = context.wrap_socket(sock)
sock_file = ssl_sock.makefile(mode='rwb')
else:
sock_file = sock.makefile(mode='rwb')
# write an incomplete request
sock_file.write(b'GET /xxx HTTP/1.0\r\n')
sock_file.flush()
# Leave it open and not doing anything
# while the other request runs to completion.
# This demonstrates that a blocking client
# doesn't hang the whole server
self._test_hello()
# now finish the original request
sock_file.write(b'\r\n')
sock_file.flush()
line = sock_file.readline()
self.assertEqual(line, b'HTTP/1.1 404 Not Found\r\n')
sock_file.close()
if ssl_sock is not None:
ssl_sock.close()
sock.close()
def test_a_blocking_client(self):
self._do_test_a_blocking_client()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,24 @@
import ssl
import gevent.testing as greentest
from gevent.testing import params
from . import test__example_wsgiserver
@greentest.skipOnCI("Timing issues sometimes lead to a connection refused")
class Test_wsgiserver_ssl(test__example_wsgiserver.Test_wsgiserver):
example = 'wsgiserver_ssl.py'
URL = 'https://%s:8443' % (params.DEFAULT_LOCAL_HOST_ADDR,)
PORT = 8443
_use_ssl = True
if hasattr(ssl, '_create_unverified_context'):
# Disable verification for our self-signed cert
# on Python >= 2.7.9 and 3.4
ssl_ctx = ssl._create_unverified_context()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,109 @@
"""
Test the contents of the ``examples/`` directory.
If an existing test in *this* directory named ``test__example_<fn>.py`` exists,
where ``<fn>`` is the base filename of an example file, it will not be tested
here.
Examples can specify that they need particular test resources to be enabled
by commenting (one per line) ``# gevent-test-requires-resource: <resource>``;
most commonly the resource will be ``network``. You can use this technique to specify
non-existant resources for things that should never be tested.
"""
import re
import os
import glob
import time
import unittest
import gevent.testing as greentest
from gevent.testing import util
this_dir = os.path.dirname(__file__)
def _find_files_to_ignore():
old_dir = os.getcwd()
try:
os.chdir(this_dir)
result = [x[14:] for x in glob.glob('test__example_*.py')]
if greentest.PYPY and greentest.RUNNING_ON_APPVEYOR:
# For some reason on Windows with PyPy, this times out,
# when it should be very fast.
result.append("processes.py")
finally:
os.chdir(old_dir)
return result
default_time_range = (2, 10)
time_ranges = { # what is this even supposed to mean? pylint:disable=consider-using-namedtuple-or-dataclass
'concurrent_download.py': (0, 30),
'processes.py': (0, default_time_range[-1])
}
class _AbstractTestMixin(util.ExampleMixin):
time_range = default_time_range
example = None
def _check_resources(self):
from gevent.testing import resources
# pylint:disable=unspecified-encoding
with open(os.path.join(self.cwd, self.example), 'r') as f:
contents = f.read()
pattern = re.compile('^# gevent-test-requires-resource: (.*)$', re.MULTILINE)
resources_needed = re.finditer(pattern, contents)
for match in resources_needed:
needed = contents[match.start(1):match.end(1)]
resources.skip_without_resource(needed)
def test_runs(self):
self._check_resources()
start = time.time()
min_time, max_time = self.time_range
self.start_kwargs = {
'timeout': max_time,
'quiet': True,
'buffer_output': True,
'nested': True,
'setenv': {'GEVENT_DEBUG': 'error'}
}
if not self.run_example():
self.fail("Failed example: " + self.example)
else:
took = time.time() - start
self.assertGreaterEqual(took, min_time)
def _build_test_classes():
result = {}
try:
example_dir = util.ExampleMixin().cwd
except unittest.SkipTest:
util.log("WARNING: No examples dir found", color='suboptimal-behaviour')
return result
ignore = _find_files_to_ignore()
for filename in glob.glob(example_dir + '/*.py'):
bn = os.path.basename(filename)
if bn in ignore:
continue
tc = type(
'Test_' + bn,
(_AbstractTestMixin, greentest.TestCase),
{
'example': bn,
'time_range': time_ranges.get(bn, _AbstractTestMixin.time_range)
}
)
result[tc.__name__] = tc
return result
for k, v in _build_test_classes().items():
locals()[k] = v
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,54 @@
import gevent
import sys
import gevent.testing as greentest
from gevent.testing import ExpectedException as ExpectedError
class RawException(Exception):
pass
def hello(err):
assert sys.exc_info() == (None, None, None), sys.exc_info()
raise err
def hello2():
try:
hello(ExpectedError('expected exception in hello'))
except ExpectedError:
pass
class Test(greentest.TestCase):
def test1(self):
error = RawException('hello')
expected_error = ExpectedError('expected exception in hello')
try:
raise error
except RawException:
self.expect_one_error()
g = gevent.spawn(hello, expected_error)
g.join()
self.assert_error(ExpectedError, expected_error)
self.assertIsInstance(g.exception, ExpectedError)
try:
raise
except: # pylint:disable=bare-except
ex = sys.exc_info()[1]
self.assertIs(ex, error)
def test2(self):
timer = gevent.get_hub().loop.timer(0)
timer.start(hello2)
try:
gevent.sleep(0.1)
self.assertEqual(sys.exc_info(), (None, None, None))
finally:
timer.close()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,54 @@
import unittest
import warnings
from gevent.testing import modules
from gevent.testing import main
from gevent.testing.sysinfo import NON_APPLICABLE_SUFFIXES
import pathlib
def make_exec_test(path, module):
path = pathlib.Path(path)
def test(_):
with open(path, 'rb') as f:
src = f.read()
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
globs = {'__file__': path, '__name__': module}
if path.name == '__init__.py':
globs['__path__'] = [path]
globs['__package__'] = module
elif '.' in module:
globs['__package__'] = module.rsplit('.', 1)[0]
try:
exec(src, globs)
except ImportError:
if module in modules.OPTIONAL_MODULES:
raise unittest.SkipTest("Unable to import optional module %s" % module)
raise
name = "test_" + module.replace(".", "_")
test.__name__ = name
return test
def make_all_tests(cls):
for path, module in modules.walk_modules(recursive=True, check_optional=False):
if module.endswith(NON_APPLICABLE_SUFFIXES):
continue
test = make_exec_test(path, module)
setattr(cls, test.__name__, test)
return cls
@make_all_tests
class Test(unittest.TestCase):
pass
if __name__ == '__main__':
# This should not be combined with other tests in the same process
# because it messes with global shared state.
# pragma: testrunner-no-combine
main()
@@ -0,0 +1,575 @@
from __future__ import print_function
from __future__ import absolute_import
import functools
import gc
import io
import os
import sys
import tempfile
import unittest
import gevent
from gevent import fileobject
from gevent._fileobjectcommon import OpenDescriptor
try:
from gevent._fileobjectposix import GreenOpenDescriptor
except ImportError:
GreenOpenDescriptor = None
import gevent.testing as greentest
from gevent.testing import sysinfo
# pylint:disable=unspecified-encoding
def Writer(fobj, line):
for character in line:
fobj.write(character)
fobj.flush()
fobj.close()
def close_fd_quietly(fd):
try:
os.close(fd)
except OSError:
pass
def skipUnlessWorksWithRegularFiles(func):
@functools.wraps(func)
def f(self):
if not self.WORKS_WITH_REGULAR_FILES:
self.skipTest("Doesn't work with regular files")
func(self)
return f
class CleanupMixin(object):
def _mkstemp(self, suffix):
fileno, path = tempfile.mkstemp(suffix)
self.addCleanup(os.remove, path)
self.addCleanup(close_fd_quietly, fileno)
return fileno, path
def _pipe(self):
r, w = os.pipe()
self.addCleanup(close_fd_quietly, r)
self.addCleanup(close_fd_quietly, w)
return r, w
class TestFileObjectBlock(CleanupMixin,
greentest.TestCase):
# serves as a base for the concurrent tests too
WORKS_WITH_REGULAR_FILES = True
def setUp(self):
super().setUp()
# Try our best to run any pending __del__ methods. When we run
# multiple tests in sequence, because file descriptor numbers get
# reused, a test could open new FDs with the same numbers as some
# object (FileObjectPosix/GreenFileDescriptorIO) still waiting for
# __del__ to get called to close the FD. If GC runs and
# __del__ gets called while that second test is running, we
# get unexpected errors.
# This shows up on PyPy 3.11.
gc.collect()
def _getTargetClass(self):
return fileobject.FileObjectBlock
def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)
def _test_del(self, **kwargs):
r, w = self._pipe()
self._do_test_del((r, w), **kwargs)
def _do_test_del(self, pipe, **kwargs):
read_fd, write_fd = pipe
writer = self._makeOne(write_fd, 'wb', **kwargs)
writer.write(b'x')
try:
writer.flush()
except IOError:
# Sometimes seen on Windows/AppVeyor
print("Failed flushing fileobject", repr(writer), file=sys.stderr)
import traceback
traceback.print_exc()
import warnings
with warnings.catch_warnings():
warnings.simplefilter('ignore', ResourceWarning)
# Deliberately getting ResourceWarning with FileObject(Thread) under Py3
del writer
# PyPy, run RawIO.__del__ to close FD.
gc.collect()
if kwargs.get("close", True):
with self.assertRaises((OSError, IOError)):
# expected, because FileObject already closed it
os.close(write_fd)
else:
os.close(write_fd)
with self._makeOne(read_fd, 'rb') as fobj:
self.assertEqual(fobj.read(), b'x')
def test_del(self):
# Close should be true by default
self._test_del()
def test_del_close(self):
self._test_del(close=True)
@skipUnlessWorksWithRegularFiles
def test_seek(self):
fileno, path = self._mkstemp('.gevent.test__fileobject.test_seek')
s = b'a' * 1024
os.write(fileno, b'B' * 15)
os.write(fileno, s)
os.close(fileno)
with open(path, 'rb') as f:
f.seek(15)
native_data = f.read(1024)
with open(path, 'rb') as f_raw:
f = self._makeOne(f_raw, 'rb', close=False)
# On Python 3, all objects should have seekable.
# On Python 2, only our custom objects do.
self.assertTrue(f.seekable())
f.seek(15)
self.assertEqual(15, f.tell())
# Note that a duplicate close() of the underlying
# file descriptor can look like an OSError from this line
# as we exit the with block
fileobj_data = f.read(1024)
self.assertEqual(native_data, s)
self.assertEqual(native_data, fileobj_data)
def __check_native_matches(self, byte_data, open_mode,
meth='read', open_path=True,
**open_kwargs):
fileno, path = self._mkstemp('.gevent_test_' + open_mode)
os.write(fileno, byte_data)
os.close(fileno)
with io.open(path, open_mode, **open_kwargs) as f:
native_data = getattr(f, meth)()
if open_path:
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
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)
return gevent_data
@skipUnlessWorksWithRegularFiles
def test_str_default_to_native(self):
# With no 'b' or 't' given, read and write native str.
gevent_data = self.__check_native_matches(b'abcdefg', 'r')
self.assertIsInstance(gevent_data, str)
@skipUnlessWorksWithRegularFiles
def test_text_encoding(self):
gevent_data = self.__check_native_matches(
u'\N{SNOWMAN}'.encode('utf-8'),
'r+',
buffering=5, encoding='utf-8'
)
self.assertIsInstance(gevent_data, str)
@skipUnlessWorksWithRegularFiles
def test_does_not_leak_on_exception(self):
# If an exception occurs during opening,
# everything still gets cleaned up.
pass
@skipUnlessWorksWithRegularFiles
def test_rbU_produces_bytes_readline(self):
if sys.version_info > (3, 11):
self.skipTest("U file mode was removed in 3.11")
# Including U in rb still produces bytes.
# Note that the universal newline behaviour is
# essentially ignored in explicit bytes mode.
gevent_data = self.__check_native_matches(
b'line1\nline2\r\nline3\rlastline\n\n',
'rbU',
meth='readlines',
)
self.assertIsInstance(gevent_data[0], bytes)
self.assertEqual(len(gevent_data), 4)
@skipUnlessWorksWithRegularFiles
def test_rU_produces_native(self):
if sys.version_info > (3, 11):
self.skipTest("U file mode was removed in 3.11")
gevent_data = self.__check_native_matches(
b'line1\nline2\r\nline3\rlastline\n\n',
'rU',
meth='readlines',
)
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):
# Issue #190, 203
r, w = os.pipe()
x = self._makeOne(r)
y = self._makeOne(w, 'w')
x.close()
y.close()
@skipUnlessWorksWithRegularFiles
@greentest.ignores_leakcheck
def test_name_after_close(self):
fileno, path = self._mkstemp('.gevent_test_named_path_after_close')
# Passing the fileno; the name is the same as the fileno, and
# doesn't change when closed.
f = self._makeOne(fileno)
nf = os.fdopen(fileno)
# On Python 2, os.fdopen() produces a name of <fdopen>;
# we follow the Python 3 semantics everywhere.
nf_name = fileno
self.assertEqual(f.name, fileno)
self.assertEqual(nf.name, nf_name)
# A file-like object that has no name; we'll close the
# `f` after this because we reuse the fileno, which
# gets passed to fcntl and so must still be valid
class Nameless(object):
def fileno(self):
return fileno
close = flush = isatty = closed = writable = lambda self: False
seekable = readable = lambda self: True
nameless = self._makeOne(Nameless(), 'rb')
with self.assertRaises(AttributeError):
getattr(nameless, 'name')
nameless.close()
with self.assertRaises(AttributeError):
getattr(nameless, 'name')
f.close()
try:
nf.close()
except OSError:
# OSError: Py3, IOError: Py2
pass
self.assertEqual(f.name, fileno)
self.assertEqual(nf.name, nf_name)
def check(arg):
f = self._makeOne(arg)
self.assertEqual(f.name, path)
f.close()
# Doesn't change after closed.
self.assertEqual(f.name, path)
# Passing the string
check(path)
# Passing an opened native object
with open(path) as nf:
check(nf)
# An io object
with io.open(path) as nf:
check(nf)
@skipUnlessWorksWithRegularFiles
def test_readinto_serial(self):
fileno, path = self._mkstemp('.gevent_test_readinto')
os.write(fileno, b'hello world')
os.close(fileno)
buf = bytearray(32)
mbuf = memoryview(buf)
def assertReadInto(byte_count, expected_data):
bytes_read = f.readinto(mbuf[:byte_count])
self.assertEqual(bytes_read, len(expected_data))
self.assertEqual(buf[:bytes_read], expected_data)
with self._makeOne(path, 'rb') as f:
assertReadInto(2, b'he')
assertReadInto(1, b'l')
assertReadInto(32, b'lo world')
assertReadInto(32, b'')
class ConcurrentFileObjectMixin(object):
# Additional tests for fileobjects that cooperate
# and we have full control of the implementation
def test_read1_binary_present(self):
# Issue #840
r, w = self._pipe()
reader = self._makeOne(r, 'rb')
self._close_on_teardown(reader)
writer = self._makeOne(w, 'w')
self._close_on_teardown(writer)
self.assertTrue(hasattr(reader, 'read1'), dir(reader))
def test_read1_text_not_present(self):
# Only defined for binary.
r, w = self._pipe()
reader = self._makeOne(r, 'rt')
self._close_on_teardown(reader)
self.addCleanup(os.close, w)
self.assertFalse(hasattr(reader, 'read1'), dir(reader))
def test_read1_default(self):
# If just 'r' is given, whether it has one or not
# depends on if we're Python 2 or 3.
r, w = self._pipe()
self.addCleanup(os.close, w)
reader = self._makeOne(r)
self._close_on_teardown(reader)
self.assertFalse(hasattr(reader, 'read1'))
def test_bufsize_0(self):
# Issue #840
r, w = self._pipe()
x = self._makeOne(r, 'rb', bufsize=0)
y = self._makeOne(w, 'wb', bufsize=0)
self._close_on_teardown(x)
self._close_on_teardown(y)
y.write(b'a')
b = x.read(1)
self.assertEqual(b, b'a')
y.writelines([b'2'])
b = x.read(1)
self.assertEqual(b, b'2')
def test_newlines(self):
import warnings
r, w = self._pipe()
lines = [b'line1\n', b'line2\r', b'line3\r\n', b'line4\r\nline5', b'\nline6']
g = gevent.spawn(Writer, self._makeOne(w, 'wb'), lines)
try:
with warnings.catch_warnings():
if sys.version_info > (3, 11):
# U is removed in Python 3.11
mode = 'r'
self.skipTest("U file mode was removed in 3.11")
else:
# U is deprecated in Python 3, shows up on FileObjectThread
warnings.simplefilter('ignore', DeprecationWarning)
mode = 'rU'
fobj = self._makeOne(r, mode)
result = fobj.read()
fobj.close()
self.assertEqual('line1\nline2\nline3\nline4\nline5\nline6', result)
finally:
g.kill()
def test_readinto(self):
# verify that .readinto() is cooperative.
# if .readinto() is not cooperative spawned greenlet will not be able
# to run and call to .readinto() will block forever.
r, w = self._pipe()
rf = self._close_on_teardown(self._makeOne(r, 'rb'))
wf = self._close_on_teardown(self._makeOne(w, 'wb'))
g = gevent.spawn(Writer, wf, [b'hello'])
try:
buf1 = bytearray(32)
buf2 = bytearray(32)
n1 = rf.readinto(buf1)
n2 = rf.readinto(buf2)
self.assertEqual(n1, 5)
self.assertEqual(buf1[:n1], b'hello')
self.assertEqual(n2, 0)
finally:
g.kill()
class TestFileObjectThread(ConcurrentFileObjectMixin, # pylint:disable=too-many-ancestors
TestFileObjectBlock):
def _getTargetClass(self):
return fileobject.FileObjectThread
def test_del_noclose(self):
# In the past, we used os.fdopen() when given a file descriptor,
# and that has a destructor that can't be bypassed, so
# close=false wasn't allowed. Now that we do everything with the
# io module, it is allowed.
self._test_del(close=False)
# We don't test this with FileObjectThread. Sometimes the
# visibility of the 'close' operation, which happens in a
# background thread, doesn't make it to the foreground
# thread in a timely fashion, leading to 'os.close(4) must
# not succeed' in test_del_close. We have the same thing
# with flushing and closing in test_newlines. Both of
# these are most commonly (only?) observed on Py27/64-bit.
# They also appear on 64-bit 3.6 with libuv
def test_del(self):
raise unittest.SkipTest("Race conditions")
def test_del_close(self):
raise unittest.SkipTest("Race conditions")
@unittest.skipUnless(
hasattr(fileobject, 'FileObjectPosix'),
"Needs FileObjectPosix"
)
class TestFileObjectPosix(ConcurrentFileObjectMixin, # pylint:disable=too-many-ancestors
TestFileObjectBlock):
if sysinfo.LIBUV and sysinfo.LINUX:
# On Linux, initializing the watcher for a regular
# file results in libuv raising EPERM. But that works
# fine on other platforms.
WORKS_WITH_REGULAR_FILES = False
def _getTargetClass(self):
return fileobject.FileObjectPosix
def test_seek_raises_ioerror(self):
# https://github.com/gevent/gevent/issues/1323
# Get a non-seekable file descriptor
r, _w = self._pipe()
with self.assertRaises(OSError) as ctx:
os.lseek(r, 0, os.SEEK_SET)
os_ex = ctx.exception
with self.assertRaises(IOError) as ctx:
f = self._makeOne(r, 'r', close=False)
# Seek directly using the underlying GreenFileDescriptorIO;
# the buffer may do different things, depending
# on the version of Python (especially 3.7+)
f.fileio.seek(0)
io_ex = ctx.exception
self.assertEqual(io_ex.errno, os_ex.errno)
self.assertEqual(io_ex.strerror, os_ex.strerror)
self.assertEqual(io_ex.args, os_ex.args)
self.assertEqual(str(io_ex), str(os_ex))
class TestTextMode(CleanupMixin, unittest.TestCase):
def test_default_mode_writes_linesep(self):
# See https://github.com/gevent/gevent/issues/1282
# libuv 1.x interferes with the default line mode on
# Windows.
# First, make sure we initialize gevent
gevent.get_hub()
fileno, path = self._mkstemp('.gevent.test__fileobject.test_default')
os.close(fileno)
with open(path, "w") as f:
f.write("\n")
with open(path, "rb") as f:
data = f.read()
self.assertEqual(data, os.linesep.encode('ascii'))
class TestOpenDescriptor(CleanupMixin, greentest.TestCase):
def _getTargetClass(self):
return OpenDescriptor
def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)
def _check(self, regex, kind, *args, **kwargs):
with self.assertRaisesRegex(kind, regex):
self._makeOne(*args, **kwargs)
case = lambda re, **kwargs: (re, TypeError, kwargs)
vase = lambda re, **kwargs: (re, ValueError, kwargs)
CASES = (
case('mode', mode=42),
case('buffering', buffering='nope'),
case('encoding', encoding=42),
case('errors', errors=42),
vase('mode', mode='aoeug'),
vase('mode U cannot be combined', mode='wU'),
vase('text and binary', mode='rtb'),
vase('append mode at once', mode='rw'),
vase('exactly one', mode='+'),
vase('take an encoding', mode='rb', encoding='ascii'),
vase('take an errors', mode='rb', errors='strict'),
vase('take a newline', mode='rb', newline='\n'),
)
def test_atomicwrite_fd(self):
from gevent._fileobjectcommon import WriteallMixin
# It basically only does something when buffering is otherwise disabled
fileno, _w = self._pipe()
desc = self._makeOne(fileno, 'wb',
buffering=0,
closefd=False,
atomic_write=True)
self.assertTrue(desc.atomic_write)
fobj = desc.opened()
self.assertIsInstance(fobj, WriteallMixin)
os.close(fileno)
def pop():
for regex, kind, kwargs in TestOpenDescriptor.CASES:
setattr(
TestOpenDescriptor, 'test_' + regex.replace(' ', '_'),
lambda self, _re=regex, _kind=kind, _kw=kwargs: self._check(_re, _kind, 1, **_kw)
)
pop()
@unittest.skipIf(GreenOpenDescriptor is None, "No support for non-blocking IO")
class TestGreenOpenDescripton(TestOpenDescriptor):
def _getTargetClass(self):
return GreenOpenDescriptor
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,11 @@
# On Python 2, a deadlock is possible if we import a module that runs gevent's getaddrinfo
# with a unicode hostname, which starts Python's getaddrinfo on a thread, which
# attempts to import encodings.idna but blocks on the import lock. Verify
# that gevent avoids this deadlock.
try:
import getaddrinfo_module # pylint:disable=import-error
except ModuleNotFoundError:
from gevent.tests import getaddrinfo_module
del getaddrinfo_module # fix pyflakes
@@ -0,0 +1,146 @@
# Copyright (c) 2006-2007, Linden Research, Inc.
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import gevent
from gevent import socket
from gevent import testing as greentest
from gevent.testing import TestCase, tcp_listener
from gevent.testing import gc_collect_if_needed
from gevent.testing import skipOnPyPy
from gevent.testing import params
PY3 = sys.version_info[0] >= 3
def _write_to_closed(f, s):
try:
r = f.write(s)
except ValueError:
assert PY3
else:
assert r is None, r
class TestGreenIo(TestCase):
def test_close_with_makefile(self):
def accept_close_early(listener):
# verify that the makefile and the socket are truly independent
# by closing the socket prior to using the made file
try:
conn, _ = listener.accept()
fd = conn.makefile(mode='wb')
conn.close()
fd.write(b'hello\n')
fd.close()
_write_to_closed(fd, b'a')
self.assertRaises(socket.error, conn.send, b'b')
finally:
listener.close()
def accept_close_late(listener):
# verify that the makefile and the socket are truly independent
# by closing the made file and then sending a character
try:
conn, _ = listener.accept()
fd = conn.makefile(mode='wb')
fd.write(b'hello')
fd.close()
conn.send(b'\n')
conn.close()
_write_to_closed(fd, b'a')
self.assertRaises(socket.error, conn.send, b'b')
finally:
listener.close()
def did_it_work(server):
client = socket.create_connection((params.DEFAULT_CONNECT, server.getsockname()[1]))
fd = client.makefile(mode='rb')
client.close()
self.assertEqual(fd.readline(), b'hello\n')
self.assertFalse(fd.read())
fd.close()
server = tcp_listener()
server_greenlet = gevent.spawn(accept_close_early, server)
did_it_work(server)
server_greenlet.kill()
server = tcp_listener()
server_greenlet = gevent.spawn(accept_close_late, server)
did_it_work(server)
server_greenlet.kill()
@skipOnPyPy("Takes multiple GCs and issues a warning we can't catch")
def test_del_closes_socket(self):
import warnings
def accept_once(listener):
# delete/overwrite the original conn
# object, only keeping the file object around
# closing the file object should close everything
# This is not *exactly* true on Python 3. This produces
# a ResourceWarning, which we silence below. (Previously we actually
# *saved* a reference to the socket object, so we
# weren't testing what we thought we were.)
# It's definitely not true on PyPy, which needs GC to
# reliably close everything; sometimes this is more than
# one collection cycle. And PyPy issues a warning with -X
# track-resources that we cannot catch.
with warnings.catch_warnings():
warnings.simplefilter('ignore')
try:
conn = listener.accept()[0]
# Note that we overwrite the original variable,
# losing our reference to the socket.
conn = conn.makefile(mode='wb')
conn.write(b'hello\n')
conn.close()
_write_to_closed(conn, b'a')
finally:
listener.close()
del listener
del conn
gc_collect_if_needed()
gc_collect_if_needed()
server = tcp_listener()
gevent.spawn(accept_once, server)
client = socket.create_connection((params.DEFAULT_CONNECT, server.getsockname()[1]))
with gevent.Timeout.start_new(0.5):
fd = client.makefile()
client.close()
self.assertEqual(fd.read(), 'hello\n')
# If the socket isn't closed when 'accept_once' finished,
# then this will hang and exceed the timeout
self.assertEqual(fd.read(), '')
fd.close()
del client
del fd
if __name__ == '__main__':
greentest.main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,183 @@
from __future__ import print_function, division, absolute_import
import time
import gevent.testing as greentest
from gevent.testing import timing
import gevent
from gevent import pool
from gevent.timeout import Timeout
DELAY = timing.LARGE_TICK
class SpecialError(Exception):
pass
class Undead(object):
def __init__(self):
self.shot_count = 0
def __call__(self):
while True:
try:
gevent.sleep(1)
except SpecialError:
break
except: # pylint:disable=bare-except
self.shot_count += 1
class Test(greentest.TestCase):
__timeout__ = greentest.LARGE_TIMEOUT
def test_basic(self):
s = pool.Group()
s.spawn(gevent.sleep, timing.LARGE_TICK)
self.assertEqual(len(s), 1, s)
s.spawn(gevent.sleep, timing.LARGE_TICK * 5)
self.assertEqual(len(s), 2, s)
gevent.sleep()
gevent.sleep(timing.LARGE_TICK * 2 + timing.LARGE_TICK_MIN_ADJ)
self.assertEqual(len(s), 1, s)
gevent.sleep(timing.LARGE_TICK * 5 + timing.LARGE_TICK_MIN_ADJ)
self.assertFalse(s)
def test_waitall(self):
s = pool.Group()
s.spawn(gevent.sleep, DELAY)
s.spawn(gevent.sleep, DELAY * 2)
assert len(s) == 2, s
start = time.time()
s.join(raise_error=True)
delta = time.time() - start
self.assertFalse(s)
self.assertEqual(len(s), 0)
self.assertTimeWithinRange(delta, DELAY * 1.9, DELAY * 2.5)
def test_kill_block(self):
s = pool.Group()
s.spawn(gevent.sleep, DELAY)
s.spawn(gevent.sleep, DELAY * 2)
assert len(s) == 2, s
start = time.time()
s.kill()
self.assertFalse(s)
self.assertEqual(len(s), 0)
delta = time.time() - start
assert delta < DELAY * 0.8, delta
def test_kill_noblock(self):
s = pool.Group()
s.spawn(gevent.sleep, DELAY)
s.spawn(gevent.sleep, DELAY * 2)
assert len(s) == 2, s
s.kill(block=False)
assert len(s) == 2, s
gevent.sleep(0.0001)
self.assertFalse(s)
self.assertEqual(len(s), 0)
def test_kill_fires_once(self):
u1 = Undead()
u2 = Undead()
p1 = gevent.spawn(u1)
p2 = gevent.spawn(u2)
def check(count1, count2):
self.assertTrue(p1)
self.assertTrue(p2)
self.assertFalse(p1.dead, p1)
self.assertFalse(p2.dead, p2)
self.assertEqual(u1.shot_count, count1)
self.assertEqual(u2.shot_count, count2)
gevent.sleep(0.01)
s = pool.Group([p1, p2])
self.assertEqual(len(s), 2, s)
check(0, 0)
s.killone(p1, block=False)
check(0, 0)
gevent.sleep(0)
check(1, 0)
s.killone(p1)
check(1, 0)
s.killone(p1)
check(1, 0)
s.kill(block=False)
s.kill(block=False)
s.kill(block=False)
check(1, 0)
gevent.sleep(DELAY)
check(1, 1)
X = object()
kill_result = gevent.with_timeout(DELAY, s.kill, block=True, timeout_value=X)
assert kill_result is X, repr(kill_result)
assert len(s) == 2, s
check(1, 1)
p1.kill(SpecialError)
p2.kill(SpecialError)
def test_killall_subclass(self):
p1 = GreenletSubclass.spawn(lambda: 1 / 0)
p2 = GreenletSubclass.spawn(lambda: gevent.sleep(10))
s = pool.Group([p1, p2])
s.kill()
def test_killall_iterable_argument_non_block(self):
p1 = GreenletSubclass.spawn(lambda: gevent.sleep(0.5))
p2 = GreenletSubclass.spawn(lambda: gevent.sleep(0.5))
s = set()
s.add(p1)
s.add(p2)
gevent.killall(s, block=False)
gevent.sleep(0.5)
for g in s:
assert g.dead
def test_killall_iterable_argument_timeout_not_started(self):
def f():
try:
gevent.sleep(1.5)
except: # pylint:disable=bare-except
gevent.sleep(1)
p1 = GreenletSubclass.spawn(f)
p2 = GreenletSubclass.spawn(f)
s = set()
s.add(p1)
s.add(p2)
gevent.killall(s, timeout=0.5)
for g in s:
self.assertTrue(g.dead, g)
def test_killall_iterable_argument_timeout_started(self):
def f():
try:
gevent.sleep(1.5)
except: # pylint:disable=bare-except
gevent.sleep(1)
p1 = GreenletSubclass.spawn(f)
p2 = GreenletSubclass.spawn(f)
s = set()
s.add(p1)
s.add(p2)
# Get them both running.
gevent.sleep(timing.SMALLEST_RELIABLE_DELAY)
with self.assertRaises(Timeout):
gevent.killall(s, timeout=0.5)
for g in s:
self.assertFalse(g.dead, g)
class GreenletSubclass(gevent.Greenlet):
pass
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,87 @@
# Copyright (c) 2008 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
Trivial test that a single process (and single thread) can both read
and write from green sockets (when monkey patched).
"""
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
from gevent import monkey
monkey.patch_all()
import gevent.testing as greentest
try:
from urllib import request as urllib2
from http.server import HTTPServer
from http.server import SimpleHTTPRequestHandler
except ImportError:
# Python 2
import urllib2
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import gevent
from gevent.testing import params
class QuietHandler(SimpleHTTPRequestHandler, object):
def log_message(self, *args): # pylint:disable=arguments-differ
self.server.messages += ((args,),)
class Server(HTTPServer, object):
messages = ()
requests_handled = 0
def __init__(self):
HTTPServer.__init__(self,
params.DEFAULT_BIND_ADDR_TUPLE,
QuietHandler)
def handle_request(self):
HTTPServer.handle_request(self)
self.requests_handled += 1
class TestGreenness(greentest.TestCase):
check_totalrefcount = False
def test_urllib2(self):
httpd = Server()
server_greenlet = gevent.spawn(httpd.handle_request)
port = httpd.socket.getsockname()[1]
rsp = urllib2.urlopen('http://127.0.0.1:%s' % port)
rsp.read()
rsp.close()
server_greenlet.join()
self.assertEqual(httpd.requests_handled, 1)
httpd.server_close()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,404 @@
# Copyright (c) 2009 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import re
import time
import unittest
import gevent.testing as greentest
import gevent.testing.timing
import gevent
from gevent import socket
from gevent.hub import Waiter, get_hub
from gevent._compat import NativeStrIO
from gevent._compat import get_this_psutil_process
DELAY = 0.1
class TestCloseSocketWhilePolling(greentest.TestCase):
def test(self):
sock = socket.socket()
self._close_on_teardown(sock)
t = get_hub().loop.timer(0)
t.start(sock.close)
with self.assertRaises(socket.error):
try:
sock.connect(('python.org', 81))
finally:
t.close()
gevent.sleep(0)
class TestExceptionInMainloop(greentest.TestCase):
def test_sleep(self):
# even if there was an error in the mainloop, the hub should continue to work
start = time.time()
gevent.sleep(DELAY)
delay = time.time() - start
delay_range = DELAY * 0.9
self.assertTimeWithinRange(delay, DELAY - delay_range, DELAY + delay_range)
error = greentest.ExpectedException('TestExceptionInMainloop.test_sleep/fail')
def fail():
raise error
with get_hub().loop.timer(0.001) as t:
t.start(fail)
self.expect_one_error()
start = time.time()
gevent.sleep(DELAY)
delay = time.time() - start
self.assert_error(value=error)
self.assertTimeWithinRange(delay, DELAY - delay_range, DELAY + delay_range)
class TestSleep(gevent.testing.timing.AbstractGenericWaitTestCase):
def wait(self, timeout):
gevent.sleep(timeout)
def test_simple(self):
gevent.sleep(0)
class TestWaiterGet(gevent.testing.timing.AbstractGenericWaitTestCase):
def setUp(self):
super(TestWaiterGet, self).setUp()
self.waiter = Waiter()
def wait(self, timeout):
with get_hub().loop.timer(timeout) as evt:
evt.start(self.waiter.switch, None)
return self.waiter.get()
class TestWaiter(greentest.TestCase):
def test(self):
waiter = Waiter()
self.assertEqual(str(waiter), '<Waiter greenlet=None>')
waiter.switch(25)
self.assertEqual(str(waiter), '<Waiter greenlet=None value=25>')
self.assertEqual(waiter.get(), 25)
waiter = Waiter()
waiter.throw(ZeroDivisionError)
assert re.match('^<Waiter greenlet=None exc_info=.*ZeroDivisionError.*$', str(waiter)), str(waiter)
self.assertRaises(ZeroDivisionError, waiter.get)
waiter = Waiter()
g = gevent.spawn(waiter.get)
g.name = 'AName'
gevent.sleep(0)
str_waiter = str(waiter)
self.assertTrue(str_waiter.startswith('<Waiter greenlet=<Greenlet "AName'),
str_waiter)
g.kill()
@greentest.skipOnCI("Racy on CI")
class TestPeriodicMonitoringThread(greentest.TestCase):
def _reset_hub(self):
hub = get_hub()
try:
del hub.exception_stream
except AttributeError:
pass
if hub._threadpool is not None:
hub.threadpool.join()
hub.threadpool.kill()
del hub.threadpool
def setUp(self):
super(TestPeriodicMonitoringThread, self).setUp()
self.monitor_thread = gevent.config.monitor_thread
gevent.config.monitor_thread = True
from gevent.monkey import get_original
self.lock = get_original('threading', 'Lock')()
self.monitor_fired = 0
self.monitored_hubs = set()
self._reset_hub()
def tearDown(self):
hub = get_hub()
if not self.monitor_thread and hub.periodic_monitoring_thread:
# If it was true, nothing to do. If it was false, tear things down.
hub.periodic_monitoring_thread.kill()
hub.periodic_monitoring_thread = None
gevent.config.monitor_thread = self.monitor_thread
self.monitored_hubs = None
self._reset_hub()
super(TestPeriodicMonitoringThread, self).tearDown()
def _monitor(self, hub):
with self.lock:
self.monitor_fired += 1
if self.monitored_hubs is not None:
self.monitored_hubs.add(hub)
def test_config(self):
self.assertEqual(0.1, gevent.config.max_blocking_time)
def _run_monitoring_threads(self, monitor, kill=True):
self.assertTrue(monitor.should_run)
from threading import Condition
cond = Condition()
cond.acquire()
def monitor_cond(_hub):
cond.acquire()
cond.notify_all()
cond.release()
if kill:
# Only run once. Especially helpful on PyPy, where
# formatting stacks is expensive.
monitor.kill()
monitor.add_monitoring_function(monitor_cond, 0.01)
cond.wait()
cond.release()
monitor.add_monitoring_function(monitor_cond, None)
@greentest.ignores_leakcheck
def test_kill_removes_trace(self):
from greenlet import gettrace
hub = get_hub()
hub.start_periodic_monitoring_thread()
self.assertIsNotNone(gettrace())
hub.periodic_monitoring_thread.kill()
self.assertIsNone(gettrace())
@greentest.ignores_leakcheck
def test_blocking_this_thread(self):
hub = get_hub()
stream = hub.exception_stream = NativeStrIO()
monitor = hub.start_periodic_monitoring_thread()
self.assertIsNotNone(monitor)
basic_monitor_func_count = 1
if get_this_psutil_process() is not None:
# psutil is installed
basic_monitor_func_count += 1
self.assertEqual(basic_monitor_func_count,
len(monitor.monitoring_functions()))
monitor.add_monitoring_function(self._monitor, 0.1)
self.assertEqual(basic_monitor_func_count + 1,
len(monitor.monitoring_functions()))
self.assertEqual(self._monitor, monitor.monitoring_functions()[-1].function)
self.assertEqual(0.1, monitor.monitoring_functions()[-1].period)
# We must make sure we have switched greenlets at least once,
# otherwise we can't detect a failure.
gevent.sleep(hub.loop.approx_timer_resolution)
assert hub.exception_stream is stream
try:
time.sleep(0.3) # Thrice the default
self._run_monitoring_threads(monitor)
finally:
monitor.add_monitoring_function(self._monitor, None)
self.assertEqual(basic_monitor_func_count,
len(monitor._monitoring_functions))
assert hub.exception_stream is stream
monitor.kill()
del hub.exception_stream
self.assertGreaterEqual(self.monitor_fired, 1)
data = stream.getvalue()
self.assertIn('appears to be blocked', data)
self.assertIn('PeriodicMonitoringThread', data)
def _prep_worker_thread(self):
hub = get_hub()
threadpool = hub.threadpool
worker_hub = threadpool.apply(get_hub)
assert hub is not worker_hub
stream = NativeStrIO()
# It does not have a monitoring thread yet
self.assertIsNone(worker_hub.periodic_monitoring_thread)
# So switch to it and give it one by letting it run.
# XXX: Python 3.10 appears to have made some changes in the memory model.
# Specifically, reading values from the background that are set in the
# background hub *from this thread* is flaky. It takes them awhile to show up.
# Really, that's correct and expected from a standard C point of view, as we
# don't insert any memory barriers or things like that. It just always used to
# work in the past. So now, rather than read them directly, we need to read them
# from the background thread itself. The same, apparently, goes for
# writing.
# Need to figure out what exactly the change was.
def task():
get_hub().exception_stream = stream
gevent.sleep(0.01)
mon = get_hub().periodic_monitoring_thread
mon.add_monitoring_function(self._monitor, 0.1)
return mon
worker_monitor = threadpool.apply(task)
self.assertIsNotNone(worker_monitor)
return worker_hub, stream, worker_monitor
@greentest.ignores_leakcheck
def test_blocking_threadpool_thread_task_queue(self):
# A threadpool thread spends much of its time
# blocked on the native Lock object. Unless we take
# care, if that thread had created a hub, it will constantly
# be reported as blocked.
worker_hub, stream, worker_monitor = self._prep_worker_thread()
# Now wait until the monitoring threads have run.
self._run_monitoring_threads(worker_monitor)
worker_monitor.kill()
# We did run the monitor in the worker thread, but it
# did NOT report itself blocked by the worker thread sitting there.
with self.lock:
self.assertIn(worker_hub, self.monitored_hubs)
self.assertEqual(stream.getvalue(), '')
@greentest.ignores_leakcheck
def test_blocking_threadpool_thread_one_greenlet(self):
# If the background threadpool thread has no other greenlets to run
# and never switches, then even if it has a hub
# we don't report it blocking. The threadpool is *meant* to run
# tasks that block.
hub = get_hub()
threadpool = hub.threadpool
worker_hub, stream, worker_monitor = self._prep_worker_thread()
task = threadpool.spawn(time.sleep, 0.3)
# Now wait until the monitoring threads have run.
self._run_monitoring_threads(worker_monitor)
# and be sure the task ran
task.get()
worker_monitor.kill()
# We did run the monitor in the worker thread, but it
# did NOT report itself blocked by the worker thread
with self.lock:
self.assertIn(worker_hub, self.monitored_hubs)
self.assertEqual(stream.getvalue(), '')
@greentest.ignores_leakcheck
def test_blocking_threadpool_thread_multi_greenlet(self):
# If the background threadpool thread ever switches
# greenlets, monitoring goes into affect.
hub = get_hub()
threadpool = hub.threadpool
worker_hub, stream, worker_monitor = self._prep_worker_thread()
def task():
g = gevent.spawn(time.sleep, 0.7)
g.join()
task = threadpool.spawn(task)
# Now wait until the monitoring threads have run.
self._run_monitoring_threads(worker_monitor, kill=False)
# and be sure the task ran
task.get()
worker_monitor.kill()
# We did run the monitor in the worker thread, and it
# DID report itself blocked by the worker thread
self.assertIn(worker_hub, self.monitored_hubs)
data = stream.getvalue()
self.assertIn('appears to be blocked', data)
self.assertIn('PeriodicMonitoringThread', data)
class TestLoopInterface(unittest.TestCase):
def test_implemensts_ILoop(self):
from gevent.testing import verify
from gevent._interfaces import ILoop
loop = get_hub().loop
verify.verifyObject(ILoop, loop)
def test_callback_implements_ICallback(self):
from gevent.testing import verify
from gevent._interfaces import ICallback
loop = get_hub().loop
cb = loop.run_callback(lambda: None)
verify.verifyObject(ICallback, cb)
def test_callback_ts_implements_ICallback(self):
from gevent.testing import verify
from gevent._interfaces import ICallback
loop = get_hub().loop
cb = loop.run_callback_threadsafe(lambda: None)
verify.verifyObject(ICallback, cb)
class TestHandleError(unittest.TestCase):
def tearDown(self):
try:
del get_hub().handle_error
except AttributeError:
pass
def test_exception_in_custom_handle_error_does_not_crash(self):
def bad_handle_error(*args):
raise AttributeError
get_hub().handle_error = bad_handle_error
class MyException(Exception):
pass
def raises():
raise MyException
with self.assertRaises(MyException):
gevent.spawn(raises).get()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,117 @@
from contextlib import contextmanager
import unittest
import gevent
from gevent.testing import ignores_leakcheck
class TestJoin(unittest.TestCase):
def test_join_many_times(self):
# hub.join() guarantees that loop has exited cleanly
res = gevent.get_hub().join()
self.assertTrue(res)
self.assertFalse(gevent.get_hub().dead)
res = gevent.get_hub().join()
self.assertTrue(res)
# but it is still possible to use gevent afterwards
gevent.sleep(0.01)
res = gevent.get_hub().join()
self.assertTrue(res)
@staticmethod
def __clean():
import gc
for _ in range(2):
while gc.collect():
pass
@contextmanager
def assert_no_greenlet_growth(self):
from gevent._greenlet_primitives import get_reachable_greenlets
clean = self.__clean
clean()
count_before = len(get_reachable_greenlets())
yield
count_after = len(get_reachable_greenlets())
if count_after > count_before:
# We could be off by exactly 1. Not entirely clear where.
# But it only happens the first time.
count_after -= 1
# If we were run in multiple process, our count could actually have
# gone down due to the GC's we did.
self.assertEqual(count_after, count_before)
@ignores_leakcheck
def test_join_in_new_thread_doesnt_leak_hub_or_greenlet(self):
# https://github.com/gevent/gevent/issues/1601
import threading
clean = self.__clean
def thread_main():
g = gevent.Greenlet(run=lambda: 0)
g.start()
g.join()
hub = gevent.get_hub()
hub.join()
hub.destroy(destroy_loop=True)
del hub
def tester(main):
t = threading.Thread(target=main)
t.start()
t.join()
clean()
with self.assert_no_greenlet_growth():
for _ in range(10):
tester(thread_main)
del tester
del thread_main
@ignores_leakcheck
def test_destroy_in_main_thread_from_new_thread(self):
# https://github.com/gevent/gevent/issues/1631
import threading
clean = self.__clean
class Thread(threading.Thread):
hub = None
def run(self):
g = gevent.Greenlet(run=lambda: 0)
g.start()
g.join()
del g
hub = gevent.get_hub()
hub.join()
self.hub = hub
def tester(Thread, clean):
t = Thread()
t.start()
t.join()
t.hub.destroy(destroy_loop=True)
t.hub = None
del t
clean()
# Unfortunately, this WILL leak greenlets,
# at least on CPython. The frames of the dead threads
# are referenced by the hub in some sort of cycle, and
# greenlets don't particpate in GC.
for _ in range(10):
tester(Thread, clean)
del tester
del Thread
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,99 @@
import functools
import unittest
import gevent
import gevent.core
from gevent.event import Event
from gevent.testing.testcase import TimeAssertMixin
SMALL_TICK = 0.05
# setting up signal does not affect join()
gevent.signal_handler(1, lambda: None) # wouldn't work on windows
def repeated(func, repetitions=2):
@functools.wraps(func)
def f(self):
for _ in range(repetitions):
func(self)
return f
class Test(TimeAssertMixin, unittest.TestCase):
@repeated
def test_callback(self):
# exiting because the spawned greenlet finished execution (spawn (=callback) variant)
x = gevent.spawn(lambda: 5)
with self.runs_in_no_time():
result = gevent.wait(timeout=10)
self.assertTrue(result)
self.assertTrue(x.dead, x)
self.assertEqual(x.value, 5)
@repeated
def test_later(self):
# exiting because the spawned greenlet finished execution (spawn_later (=timer) variant)
x = gevent.spawn_later(SMALL_TICK, lambda: 5)
with self.runs_in_given_time(SMALL_TICK):
result = gevent.wait(timeout=10)
self.assertTrue(result)
self.assertTrue(x.dead, x)
@repeated
def test_timeout(self):
# exiting because of timeout (the spawned greenlet still runs)
x = gevent.spawn_later(10, lambda: 5)
with self.runs_in_given_time(SMALL_TICK):
result = gevent.wait(timeout=SMALL_TICK)
self.assertFalse(result)
self.assertFalse(x.dead, x)
x.kill()
with self.runs_in_no_time():
result = gevent.wait()
self.assertTrue(result)
@repeated
def test_event(self):
# exiting because of event (the spawned greenlet still runs)
x = gevent.spawn_later(10, lambda: 5)
event = Event()
event_set = gevent.spawn_later(SMALL_TICK, event.set)
with self.runs_in_given_time(SMALL_TICK):
result = gevent.wait([event])
self.assertEqual(result, [event])
self.assertFalse(x.dead, x)
self.assertTrue(event_set.dead)
self.assertTrue(event.is_set)
x.kill()
with self.runs_in_no_time():
result = gevent.wait()
self.assertTrue(result)
@repeated
def test_ref_arg(self):
# checking "ref=False" argument
gevent.get_hub().loop.timer(10, ref=False).start(lambda: None)
with self.runs_in_no_time():
result = gevent.wait()
self.assertTrue(result)
@repeated
def test_ref_attribute(self):
# checking "ref=False" attribute
w = gevent.get_hub().loop.timer(10)
w.start(lambda: None)
w.ref = False
with self.runs_in_no_time():
result = gevent.wait()
self.assertTrue(result)
class TestAgain(Test):
"Repeat the same tests"
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,42 @@
#!/usr/bin/python
# See https://github.com/gevent/gevent/issues/108
import gevent
from gevent import monkey
import_errors = []
def some_func():
try:
from _blocks_at_top_level import x
assert x == 'done'
except ImportError as e:
import_errors.append(e)
raise
if __name__ == '__main__':
import sys
if sys.version_info[:2] == (3, 13):
import unittest
class Test(unittest.TestCase):
def test_it(self):
self.skipTest(
'On Python 3.13, no matter how I arrange the PYTHONPATH/sys.path '
'we get "cannot import name x from partially initialized module '
'_blocks_at_top_level". It is unclear why. Limiting the scope of '
'the exclusion for now.'
)
unittest.main()
else:
monkey.patch_all()
import sys
import os
p = os.path.dirname(__file__)
sys.path.insert(0, p)
gs = [gevent.spawn(some_func) for i in range(2)]
gevent.joinall(gs)
assert not import_errors, import_errors
@@ -0,0 +1,10 @@
# https://github.com/gevent/gevent/issues/652 and 651
from gevent import monkey
monkey.patch_all()
try:
import _import_wait # pylint:disable=import-error
except ModuleNotFoundError:
from gevent.tests import _import_wait
assert _import_wait.x
@@ -0,0 +1,19 @@
import sys
import unittest
import threading
import gevent
import gevent.monkey
gevent.monkey.patch_all()
@unittest.skipUnless(
sys.version_info[0] == 2,
"Only on Python 2"
)
class Test(unittest.TestCase):
def test(self):
self.assertIs(threading._sleep, gevent.sleep)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
"""
Tests for https://github.com/gevent/gevent/issues/1686
which is about destroying a hub when there are active
callbacks or IO in operation.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import unittest
from gevent import testing as greentest
# Don't let the testrunner put us in a process with other
# tests; we are strict on the state of the hub and greenlets.
# pragma: testrunner-no-combine
@greentest.skipOnWindows("Uses os.fork")
class TestDestroyInChildWithActiveSpawn(unittest.TestCase):
def test(self): # pylint:disable=too-many-locals
# If this test is broken, there are a few failure modes.
# - In the original examples, the parent process just hangs, because the
# child has raced ahead, spawned the greenlet and read the data. When the
# greenlet goes to read in the parent, it blocks, and the hub and loop
# wait for it.
# - Here, our child detects the greenlet ran when it shouldn't and
# raises an error, which translates to a non-zero exit status,
# which the parent checks for and fails by raising an exception before
# returning control to the hub. We can replicate the hang by removing the
# assertion in the child.
from time import sleep as hang
from gevent import get_hub
from gevent import spawn
from gevent.socket import wait_read
from gevent.os import nb_read
from gevent.os import nb_write
from gevent.os import make_nonblocking
from gevent.os import fork
from gevent.os import waitpid
pipe_read_fd, pipe_write_fd = os.pipe()
make_nonblocking(pipe_read_fd)
make_nonblocking(pipe_write_fd)
run = []
def reader():
run.append(1)
return nb_read(pipe_read_fd, 4096)
# Put data in the pipe
DATA = b'test'
nb_write(pipe_write_fd, DATA)
# Make sure we're ready to read it
wait_read(pipe_read_fd)
# Schedule a greenlet to start
reader = spawn(reader)
hub = get_hub()
pid = fork()
if pid == 0:
# Child destroys the hub. The reader should not have run.
hub.destroy(destroy_loop=True)
self.assertFalse(run)
os._exit(0)
return # pylint:disable=unreachable
# The parent.
# Briefly prevent us from spinning our event loop.
hang(0.5)
wait_child_result = waitpid(pid, 0)
self.assertEqual(wait_child_result, (pid, 0))
# We should get the data; the greenlet only runs in the parent.
data = reader.get()
self.assertEqual(run, [1])
self.assertEqual(data, DATA)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,28 @@
import sys
import unittest
from gevent import testing as greentest
class TestSubnormalFloatsAreNotDisabled(unittest.TestCase):
@greentest.skipOnCI('Some of our tests we compile with -Ofast, which breaks this.')
def test_subnormal_is_not_zero(self):
# Enabling the -Ofast compiler flag resulted in subnormal floats getting
# disabled the moment when gevent was imported. This impacted libraries
# that expect subnormal floats to be enabled.
#
# NOTE: This test is supposed to catch that. It doesn't seem to work perfectly, though.
# The test passes under Python 2 on macOS no matter whether -ffast-math is given or not;
# perhaps this is a difference in clang vs gcc? In contrast, the test on Python 2.7 always
# *fails* on GitHub actions (in both CPython 2.7 and PyPy). We're far past the EOL of
# Python 2.7 so I'm not going to spend much time investigating.
__import__('gevent')
# `sys.float_info.min` is the minimum representable positive normalized
# float, so dividing it by two gives us a positive subnormal float,
# as long as subnormals floats are not disabled.
self.assertGreater(sys.float_info.min / 2, 0.0)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,27 @@
import gevent.monkey
gevent.monkey.patch_all()
import socket
import multiprocessing
from gevent import testing as greentest
# Make sure that using the resolver in a forked process
# doesn't hang forever.
def block():
socket.getaddrinfo('localhost', 8001)
class Test(greentest.TestCase):
def test(self):
socket.getaddrinfo('localhost', 8001)
p = multiprocessing.Process(target=block)
p.start()
p.join()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,82 @@
# A greenlet that's killed before it is ever started
# should never be switched to
import gevent
import gevent.testing as greentest
class MyException(Exception):
pass
class TestSwitch(greentest.TestCase):
def setUp(self):
super(TestSwitch, self).setUp()
self.switched_to = [False, False]
self.caught = None
def should_never_run(self, i): # pragma: no cover
self.switched_to[i] = True
def check(self, g, g2):
gevent.joinall((g, g2))
self.assertEqual([False, False], self.switched_to)
# They both have a GreenletExit as their value
self.assertIsInstance(g.value, gevent.GreenletExit)
self.assertIsInstance(g2.value, gevent.GreenletExit)
# They both have no reported exc_info
self.assertIsNone(g.exc_info)
self.assertIsNone(g2.exc_info)
self.assertIsNone(g.exception)
self.assertIsNone(g2.exception)
def test_gevent_kill(self):
g = gevent.spawn(self.should_never_run, 0) # create but do not switch to
g2 = gevent.spawn(self.should_never_run, 1) # create but do not switch to
# Using gevent.kill
gevent.kill(g)
gevent.kill(g2)
self.check(g, g2)
def test_greenlet_kill(self):
# killing directly
g = gevent.spawn(self.should_never_run, 0)
g2 = gevent.spawn(self.should_never_run, 1)
g.kill()
g2.kill()
self.check(g, g2)
def test_throw(self):
# throwing
g = gevent.spawn(self.should_never_run, 0)
g2 = gevent.spawn(self.should_never_run, 1)
g.throw(gevent.GreenletExit)
g2.throw(gevent.GreenletExit)
self.check(g, g2)
def catcher(self):
try:
while True:
gevent.sleep(0)
except MyException as e:
self.caught = e
def test_kill_exception(self):
# Killing with gevent.kill gets the right exception,
# and we can pass exception objects, not just exception classes.
g = gevent.spawn(self.catcher)
g.start()
gevent.sleep()
gevent.kill(g, MyException())
gevent.sleep()
self.assertIsInstance(self.caught, MyException)
self.assertIsNone(g.exception, MyException)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,40 @@
import gevent
from gevent import testing as greentest
#import socket # on windows
# iwait should not raise `LoopExit: This operation would block forever`
# or `AssertionError: Invalid switch into ...`
# if the caller of iwait causes greenlets to switch in between
# return values
def worker(i):
# Have one of them raise an exception to test that case
if i == 2:
raise ValueError(i)
return i
class Test(greentest.TestCase):
def test(self):
finished = 0
# Wait on a group that includes one that will already be
# done, plus some that will finish as we watch
done_worker = gevent.spawn(worker, "done")
gevent.joinall((done_worker,))
workers = [gevent.spawn(worker, i) for i in range(3)]
workers.append(done_worker)
for _ in gevent.iwait(workers):
finished += 1
# Simulate doing something that causes greenlets to switch;
# a non-zero timeout is crucial
try:
gevent.sleep(0.01)
except ValueError as ex:
self.assertEqual(ex.args[0], 2)
self.assertEqual(finished, 4)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,39 @@
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import sys
if not sys.argv[1:]:
from subprocess import Popen, PIPE
# not on Py2 pylint:disable=consider-using-with
p = Popen([sys.executable, __file__, 'subprocess'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
out, err = p.communicate(b'hello world\n')
code = p.poll()
assert p.poll() == 0, (out, err, code)
assert out.strip() == b'11 chars.', (out, err, code)
# XXX: This is seen sometimes to fail on Travis with the following value in err but a code of 0;
# it seems load related:
# 'Unhandled exception in thread started by \nsys.excepthook is missing\nlost sys.stderr\n'.
# If warnings are enabled, Python 3 has started producing this:
# '...importlib/_bootstrap.py:219: ImportWarning: can't resolve package from __spec__
# or __package__, falling back on __name__ and __path__\n return f(*args, **kwds)\n'
assert err == b'' or b'sys.excepthook' in err or b'Warning' in err, (out, err, code)
elif sys.argv[1:] == ['subprocess']: # pragma: no cover
import gevent
import gevent.monkey
gevent.monkey.patch_all(sys=True)
def printline():
try:
line = raw_input()
except NameError:
line = input() # pylint:disable=bad-builtin
print('%s chars.' % len(line))
sys.stdout.flush()
gevent.spawn(printline).join()
else: # pragma: no cover
sys.exit('Invalid arguments: %r' % (sys.argv, ))
@@ -0,0 +1,48 @@
# Make sure that libev child watchers, implicitly installed through the use
# of subprocess, do not cause waitpid() to fail to poll for processes.
# NOTE: This was only reproducible under python 2.
from __future__ import print_function
import gevent
from gevent import monkey
monkey.patch_all()
import sys
from multiprocessing import Process
from subprocess import Popen, PIPE
from gevent import testing as greentest
def f(sleep_sec):
gevent.sleep(sleep_sec)
class TestIssue600(greentest.TestCase):
__timeout__ = greentest.LARGE_TIMEOUT
@greentest.skipOnLibuvOnPyPyOnWin("hangs")
def test_invoke(self):
# Run a subprocess through Popen to make sure
# libev is handling SIGCHLD. This could *probably* be simplified to use
# just hub.loop.install_sigchld
# (no __enter__/__exit__ on Py2) pylint:disable=consider-using-with
p = Popen([sys.executable, '-V'], stdout=PIPE, stderr=PIPE)
gevent.sleep(0)
p.communicate()
gevent.sleep(0)
def test_process(self):
# Launch
p = Process(target=f, args=(0.5,))
p.start()
with gevent.Timeout(3):
# Poll for up to 10 seconds. If the bug exists,
# this will timeout because our subprocess should
# be long gone by now
p.join(10)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,47 @@
# A greenlet that's killed with an exception should fail.
import gevent.testing as greentest
import gevent
class ExpectedError(greentest.ExpectedException):
pass
def f():
gevent.sleep(999)
class TestKillWithException(greentest.TestCase):
def test_kill_without_exception(self):
g = gevent.spawn(f)
g.kill()
assert g.successful()
assert isinstance(g.get(), gevent.GreenletExit)
def test_kill_with_exception(self):
# issue-607 pointed this case.
g = gevent.spawn(f)
with gevent.get_hub().ignoring_expected_test_error():
# Hmm, this only needs the `with ignoring...` in
# PURE_PYTHON mode (or PyPy).
g.kill(ExpectedError)
self.assertFalse(g.successful())
self.assertRaises(ExpectedError, g.get)
self.assertIsNone(g.value)
self.assertIsInstance(g.exception, ExpectedError)
def test_kill_with_exception_after_started(self):
with gevent.get_hub().ignoring_expected_test_error():
g = gevent.spawn(f)
g.join(0)
g.kill(ExpectedError)
self.assertFalse(g.successful())
self.assertRaises(ExpectedError, g.get)
self.assertIsNone(g.value)
self.assertIsInstance(g.exception, ExpectedError)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,12 @@
# Test idle
import gevent
from gevent import testing as greentest
class Test(greentest.TestCase):
def test(self):
gevent.sleep()
gevent.idle()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,9 @@
#!/usr/bin/env python
from gevent.monkey import patch_all
patch_all()
if __name__ == '__main__':
# Reproducing #728 requires a series of nested
# imports
__import__('_imports_imports_at_top_level')
@@ -0,0 +1,95 @@
'''Test for GitHub issues 461 and 471.
When moving to Python 3, handling of KeyboardInterrupt exceptions caused
by a Ctrl-C raised an exception while printing the traceback for a
greenlet preventing the process from exiting. This test tests for proper
handling of KeyboardInterrupt.
'''
import sys
if sys.argv[1:] == ['subprocess']: # pragma: no cover
import gevent
def task():
sys.stdout.write('ready\n')
sys.stdout.flush()
gevent.sleep(30)
try:
gevent.spawn(task).get()
except KeyboardInterrupt:
pass
sys.exit(0)
else:
import signal
from subprocess import Popen, PIPE
import time
import unittest
import gevent.testing as greentest
from gevent.testing.sysinfo import CFFI_BACKEND
from gevent.testing.sysinfo import RUN_COVERAGE
from gevent.testing.sysinfo import WIN
from gevent.testing.sysinfo import PYPY3
class Test(unittest.TestCase):
@unittest.skipIf(
(CFFI_BACKEND and RUN_COVERAGE) or (PYPY3 and WIN),
"Interferes with the timing; times out waiting for the child")
def test_hang(self):
# XXX: Why does PyPy3 on Win fail to kill the child? (This was before we switched
# to pypy3w; perhaps that makes a difference?)
if WIN:
from subprocess import CREATE_NEW_PROCESS_GROUP
kwargs = {'creationflags': CREATE_NEW_PROCESS_GROUP}
else:
kwargs = {}
# (not on Py2) pylint:disable=consider-using-with
p = Popen([sys.executable, __file__, 'subprocess'], stdout=PIPE, **kwargs)
line = p.stdout.readline()
if not isinstance(line, str):
line = line.decode('ascii')
# Windows needs the \n in the string to write (because of buffering), but
# because of newline handling it doesn't make it through the read; whereas
# it does on other platforms. Universal newlines is broken on Py3, so the best
# thing to do is to strip it
line = line.strip()
self.assertEqual(line, 'ready')
# On Windows, we have to send the CTRL_BREAK_EVENT (which seems to terminate the process); SIGINT triggers
# "ValueError: Unsupported signal: 2". The CTRL_C_EVENT is ignored on Python 3 (but not Python 2).
# So this test doesn't test much on Windows.
signal_to_send = signal.SIGINT if not WIN else getattr(signal, 'CTRL_BREAK_EVENT')
p.send_signal(signal_to_send)
# Wait a few seconds for child process to die. Sometimes signal delivery is delayed
# or even swallowed by Python, so send the signal a few more times if necessary
wait_seconds = 25.0
now = time.time()
midtime = now + (wait_seconds / 2.0)
endtime = time.time() + wait_seconds
while time.time() < endtime:
if p.poll() is not None:
break
if time.time() > midtime:
p.send_signal(signal_to_send)
midtime = endtime + 1 # only once
time.sleep(0.1)
else:
# Kill unresponsive child and exit with error 1
p.terminate()
p.wait()
raise AssertionError("Failed to wait for child")
# If we get here, it's because we caused the process to exit; it
# didn't hang. Under Windows, however, we have to use CTRL_BREAK_EVENT,
# which has an arbitrary returncode depending on versions (so does CTRL_C_EVENT
# on Python 2). We still
# count this as success.
self.assertEqual(p.returncode if not WIN else 0, 0)
p.stdout.close()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,42 @@
import gevent
import gevent.testing as greentest
from gevent.lock import Semaphore
class Testiwait(greentest.TestCase):
def test_noiter(self):
# Test that gevent.iwait returns objects which can be iterated upon
# without additional calls to iter()
sem1 = Semaphore()
sem2 = Semaphore()
gevent.spawn(sem1.release)
ready = next(gevent.iwait((sem1, sem2)))
self.assertEqual(sem1, ready)
def test_iwait_partial(self):
# Test that the iwait context manager allows the iterator to be
# consumed partially without a memory leak.
sem = Semaphore()
let = gevent.spawn(sem.release)
with gevent.iwait((sem,), timeout=0.01) as iterator:
self.assertEqual(sem, next(iterator))
let.get()
def test_iwait_nogarbage(self):
sem1 = Semaphore()
sem2 = Semaphore()
let = gevent.spawn(sem1.release)
with gevent.iwait((sem1, sem2)) as iterator:
self.assertEqual(sem1, next(iterator))
self.assertEqual(sem2.linkcount(), 1)
self.assertEqual(sem2.linkcount(), 0)
let.get()
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,20 @@
import gevent
from gevent import testing as greentest
class Test(greentest.TestCase):
def test(self):
def func():
pass
a = gevent.spawn(func)
b = gevent.spawn(func)
gevent.joinall([a, b, a])
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,425 @@
import gevent.testing as greentest
from copy import copy
# Comment the line below to see that the standard thread.local is working correct
from gevent import monkey; monkey.patch_all()
from threading import local
from threading import Thread
from zope import interface
try:
from collections.abc import Mapping
except ImportError:
from collections import Mapping # pylint:disable=deprecated-class
class ReadProperty(object):
"""A property that can be overridden"""
# A non-data descriptor
def __get__(self, inst, klass):
return 42 if inst is not None else self
class A(local):
__slots__ = ['initialized', 'obj']
path = ''
type_path = 'MyPath'
read_property = ReadProperty()
def __init__(self, obj):
super(A, self).__init__()
if not hasattr(self, 'initialized'):
self.obj = obj
self.path = ''
class Obj(object):
pass
# These next two classes have to be global to avoid the leakchecks
deleted_sentinels = []
created_sentinels = []
class Sentinel(object):
def __del__(self):
deleted_sentinels.append(id(self))
class MyLocal(local):
CLASS_PROP = 42
def __init__(self):
local.__init__(self)
self.sentinel = Sentinel()
created_sentinels.append(id(self.sentinel))
@property
def desc(self):
return self
class MyLocalSubclass(MyLocal):
pass
class WithGetattr(local):
def __getattr__(self, name):
if name == 'foo':
return 42
return super(WithGetattr, self).__getattr__(name) # pylint:disable=no-member
class LocalWithABC(local, Mapping):
def __getitem__(self, name):
return self.d[name]
def __iter__(self):
return iter(self.d)
def __len__(self):
return len(self.d)
class LocalWithStaticMethod(local):
@staticmethod
def a_staticmethod():
return 42
class LocalWithClassMethod(local):
@classmethod
def a_classmethod(cls):
return cls
class TestGeventLocal(greentest.TestCase):
# pylint:disable=attribute-defined-outside-init,blacklisted-name
def setUp(self):
del deleted_sentinels[:]
del created_sentinels[:]
tearDown = setUp
def test_create_local_subclass_init_args(self):
with self.assertRaisesRegex(TypeError,
"Initialization arguments are not supported"):
local("foo")
with self.assertRaisesRegex(TypeError,
"Initialization arguments are not supported"):
local(kw="foo")
def test_local_opts_not_subclassed(self):
l = local()
l.attr = 1
self.assertEqual(l.attr, 1)
def test_cannot_set_delete_dict(self):
l = local()
with self.assertRaises(AttributeError):
l.__dict__ = 1
with self.assertRaises(AttributeError):
del l.__dict__
def test_delete_with_no_dict(self):
l = local()
with self.assertRaises(AttributeError):
delattr(l, 'thing')
def del_local():
with self.assertRaises(AttributeError):
delattr(l, 'thing')
t = Thread(target=del_local)
t.start()
t.join()
def test_slot_and_type_attributes(self):
a = A(Obj())
a.initialized = 1
self.assertEqual(a.initialized, 1)
# The slot is shared
def demonstrate_slots_shared():
self.assertEqual(a.initialized, 1)
a.initialized = 2
greenlet = Thread(target=demonstrate_slots_shared)
greenlet.start()
greenlet.join()
self.assertEqual(a.initialized, 2)
# The slot overrides dict values
a.__dict__['initialized'] = 42 # pylint:disable=unsupported-assignment-operation
self.assertEqual(a.initialized, 2)
# Deleting the slot deletes the slot, but not the dict
del a.initialized
self.assertFalse(hasattr(a, 'initialized'))
self.assertIn('initialized', a.__dict__)
# We can delete the 'path' ivar
# and fall back to the type
del a.path
self.assertEqual(a.path, '')
with self.assertRaises(AttributeError):
del a.path
# A read property calls get
self.assertEqual(a.read_property, 42)
a.read_property = 1
self.assertEqual(a.read_property, 1)
self.assertIsInstance(A.read_property, ReadProperty)
# Type attributes can be read
self.assertEqual(a.type_path, 'MyPath')
self.assertNotIn('type_path', a.__dict__)
# and replaced in the dict
a.type_path = 'Local'
self.assertEqual(a.type_path, 'Local')
self.assertIn('type_path', a.__dict__)
def test_attribute_error(self):
# pylint:disable=attribute-defined-outside-init
a = A(Obj())
with self.assertRaises(AttributeError):
getattr(a, 'fizz_buzz')
def set_fizz_buzz():
a.fizz_buzz = 1
greenlet = Thread(target=set_fizz_buzz)
greenlet.start()
greenlet.join()
with self.assertRaises(AttributeError):
getattr(a, 'fizz_buzz')
def test_getattr_called(self):
getter = WithGetattr()
self.assertEqual(42, getter.foo)
getter.foo = 'baz'
self.assertEqual('baz', getter.foo)
def test_copy(self):
a = A(Obj())
a.path = '123'
a.obj.echo = 'test'
b = copy(a)
# Copy makes a shallow copy. Meaning that the attribute path
# has to be independent in the original and the copied object because the
# value is a string, but the attribute obj should be just reference to
# the instance of the class Obj
self.assertEqual(a.path, b.path, 'The values in the two objects must be equal')
self.assertEqual(a.obj, b.obj, 'The values must be equal')
b.path = '321'
self.assertNotEqual(a.path, b.path, 'The values in the two objects must be different')
a.obj.echo = "works"
self.assertEqual(a.obj, b.obj, 'The values must be equal')
def test_copy_no_subclass(self):
a = local()
setattr(a, 'thing', 42)
b = copy(a)
self.assertEqual(b.thing, 42)
self.assertIsNot(a.__dict__, b.__dict__)
def test_objects(self):
# Test which failed in the eventlet?!
a = A({})
a.path = '123'
b = A({'one': 2})
b.path = '123'
self.assertEqual(a.path, b.path, 'The values in the two objects must be equal')
b.path = '321'
self.assertNotEqual(a.path, b.path, 'The values in the two objects must be different')
def test_class_attr(self, kind=MyLocal):
mylocal = kind()
self.assertEqual(42, mylocal.CLASS_PROP)
mylocal.CLASS_PROP = 1
self.assertEqual(1, mylocal.CLASS_PROP)
self.assertEqual(mylocal.__dict__['CLASS_PROP'], 1) # pylint:disable=unsubscriptable-object
del mylocal.CLASS_PROP
self.assertEqual(42, mylocal.CLASS_PROP)
self.assertIs(mylocal, mylocal.desc)
def test_class_attr_subclass(self):
self.test_class_attr(kind=MyLocalSubclass)
def test_locals_collected_when_greenlet_dead_but_still_referenced(self):
# https://github.com/gevent/gevent/issues/387
import gevent
my_local = MyLocal()
my_local.sentinel = None
greentest.gc_collect_if_needed()
del created_sentinels[:]
del deleted_sentinels[:]
def demonstrate_my_local():
# Get the important parts
getattr(my_local, 'sentinel')
# Create and reference greenlets
greenlets = [Thread(target=demonstrate_my_local) for _ in range(5)]
for t in greenlets:
t.start()
gevent.sleep()
self.assertEqual(len(created_sentinels), len(greenlets))
for g in greenlets:
assert not g.is_alive()
gevent.sleep() # let the callbacks run
greentest.gc_collect_if_needed()
# The sentinels should be gone too
self.assertEqual(len(deleted_sentinels), len(greenlets))
@greentest.skipOnLibuvOnPyPyOnWin("GC makes this non-deterministic, especially on Windows")
def test_locals_collected_when_unreferenced_even_in_running_greenlet(self):
# In fact only on Windows do we see GC being an issue;
# pypy2 5.0 on macos and travis don't have a problem.
# https://github.com/gevent/gevent/issues/981
import gevent
import gc
gc.collect()
count = 1000
running_greenlet = None
def demonstrate_my_local():
for _ in range(1000):
x = MyLocal()
self.assertIsNotNone(x.sentinel)
x = None
gc.collect()
gc.collect()
self.assertEqual(count, len(created_sentinels))
# They're all dead, even though this greenlet is
# still running
self.assertEqual(count, len(deleted_sentinels))
# The links were removed as well.
self.assertFalse(running_greenlet.has_links())
running_greenlet = gevent.spawn(demonstrate_my_local)
gevent.sleep()
running_greenlet.join()
self.assertEqual(count, len(deleted_sentinels))
@greentest.ignores_leakcheck
def test_local_dicts_for_greenlet(self):
import gevent
from gevent.local import all_local_dicts_for_greenlet
class MyGreenlet(gevent.Greenlet):
results = None
id_x = None
def _run(self): # pylint:disable=method-hidden
x = local()
x.foo = 42
self.id_x = id(x)
self.results = all_local_dicts_for_greenlet(self)
g = MyGreenlet()
g.start()
g.join()
self.assertTrue(g.successful, g)
self.assertEqual(g.results,
[((local, g.id_x), {'foo': 42})])
def test_local_with_abc(self):
# an ABC (or generally any non-exact-type) in the MRO doesn't
# break things. See https://github.com/gevent/gevent/issues/1201
x = LocalWithABC()
x.d = {'a': 1}
self.assertEqual({'a': 1}, x.d)
# The ABC part works
self.assertIn('a', x.d)
self.assertEqual(['a'], list(x.keys()))
def test_local_with_staticmethod(self):
x = LocalWithStaticMethod()
self.assertEqual(42, x.a_staticmethod())
def test_local_with_classmethod(self):
x = LocalWithClassMethod()
self.assertIs(LocalWithClassMethod, x.a_classmethod())
class TestLocalInterface(greentest.TestCase):
__timeout__ = None
@greentest.ignores_leakcheck
def test_provides(self):
# https://github.com/gevent/gevent/issues/1122
# pylint:disable=inherit-non-class
class IFoo(interface.Interface):
pass
@interface.implementer(IFoo)
class Base(object):
pass
class Derived(Base, local):
pass
d = Derived()
p = list(interface.providedBy(d))
self.assertEqual([IFoo], p)
@greentest.skipOnPurePython("Needs C extension")
class TestCExt(greentest.TestCase): # pragma: no cover
def test_c_extension(self):
self.assertEqual(local.__module__,
'gevent._gevent_clocal')
@greentest.skipWithCExtensions("Needs pure-python")
class TestPure(greentest.TestCase):
def test_extension(self):
self.assertEqual(local.__module__,
'gevent.local')
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,78 @@
from gevent import lock
import gevent.testing as greentest
from gevent.tests import test__semaphore
class TestRLockMultiThread(test__semaphore.TestSemaphoreMultiThread):
def _makeOne(self):
# If we don't set the hub before returning,
# there's a potential race condition, if the implementation
# isn't careful. If it's the background hub that winds up capturing
# the hub, it will ask the hub to switch back to itself and
# then switch to the hub, which will raise LoopExit (nothing
# for the background thread to do). What is supposed to happen
# is that the background thread realizes it's the background thread,
# starts an async watcher and then switches to the hub.
#
# So we deliberately don't set the hub to help test that condition.
return lock.RLock()
def assertOneHasNoHub(self, sem):
self.assertIsNone(sem._block.hub)
class TestLockReinitAfterFork(greentest.TestCase):
def test_it(self):
# See https://github.com/gevent/gevent/issues/1895
# Make sure we carefully handle forking while running callbacks
# of a lock/_AbstractLinkable object.
import sys
import subprocess
import textwrap
import os
if not hasattr(os, 'fork'):
self.skipTest('Requires os.fork')
script =textwrap.dedent("""\
from gevent import monkey
monkey.patch_all()
from gevent import spawn
import os
from logging import Handler
from threading import Event
handler = Handler()
handler.acquire()
event = Event()
def forker():
event.set()
handler.acquire()
handler.release()
os.fork()
g = spawn(forker)
event.wait()
handler.release()
g.join()
""")
output = subprocess.check_output(
[sys.executable, '-c', script],
stderr=subprocess.STDOUT,
).decode('utf-8')
# We used to print a failing AssertionError to stdout,
# that should no longer be the case.
self.assertFalse(output)
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,18 @@
from gevent import get_hub
from gevent import testing as greentest
class Test(greentest.TestCase):
def test(self):
count = [0]
def incr():
count[0] += 1
loop = get_hub().loop
loop.run_callback(incr)
loop.run()
self.assertEqual(count, [1])
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,553 @@
from __future__ import print_function
import os
from gevent import monkey; monkey.patch_all()
import socket
import ssl
import threading
import errno
import gevent.testing as greentest
from gevent.testing.params import DEFAULT_BIND_ADDR_TUPLE
from gevent.testing.params import DEFAULT_CONNECT
from gevent.testing.sockets import tcp_listener
dirname = os.path.dirname(os.path.abspath(__file__))
CERTFILE = os.path.join(dirname, '2_7_keycert.pem')
pid = os.getpid()
PYPY = greentest.PYPY
CPYTHON = not PYPY
fd_types = int
WIN = greentest.WIN
from gevent.testing import get_open_files
try:
import psutil
except ImportError:
psutil = None
# wrap_socket() is considered deprecated in 3.9
# pylint:disable=deprecated-method
class Test(greentest.TestCase):
extra_allowed_open_states = ()
def tearDown(self):
self.extra_allowed_open_states = ()
super(Test, self).tearDown()
def assert_raises_EBADF(self, func):
try:
result = func()
except OSError as ex:
# Windows/Py3 raises "OSError: [WinError 10038]"
if ex.args[0] == errno.EBADF:
return
if WIN and ex.args[0] == 10038:
return
raise
raise AssertionError('NOT RAISED EBADF: %r() returned %r' % (func, result))
if WIN or (PYPY and greentest.LINUX):
def __assert_fd_open(self, fileno):
# We can't detect open file descriptors on Windows.
# On PyPy 3.6-7.3 on Travis CI (linux), for some reason the
# client file descriptors don't always show as open. Don't know why,
# was fine in 7.2.
# On March 23 2020 we had to pin psutil back to a version
# for PyPy 2 (see setup.py) and this same problem started happening there.
# PyPy on macOS was unaffected.
pass
else:
def __assert_fd_open(self, fileno):
assert isinstance(fileno, fd_types)
open_files = get_open_files()
if fileno not in open_files:
raise AssertionError('%r is not open:\n%s' % (fileno, open_files['data']))
def assert_fd_closed(self, fileno):
assert isinstance(fileno, fd_types), repr(fileno)
assert fileno > 0, fileno
# Here, if we're in the process of closing, don't consider it open.
# This goes into details of psutil
open_files = get_open_files(count_closing_as_open=False)
if fileno in open_files:
raise AssertionError('%r is not closed:\n%s' % (fileno, open_files['data']))
def _assert_sock_open(self, sock):
# requires the psutil output
open_files = get_open_files()
sockname = sock.getsockname()
for x in open_files['data']:
if getattr(x, 'laddr', None) == sockname:
assert x.status in (psutil.CONN_LISTEN, psutil.CONN_ESTABLISHED) + self.extra_allowed_open_states, x.status
return
raise AssertionError("%r is not open:\n%s" % (sock, open_files['data']))
def assert_open(self, sock, *rest):
if isinstance(sock, fd_types):
self.__assert_fd_open(sock)
else:
fileno = sock.fileno()
assert isinstance(fileno, fd_types), fileno
sockname = sock.getsockname()
assert isinstance(sockname, tuple), sockname
if not WIN:
self.__assert_fd_open(fileno)
else:
self._assert_sock_open(sock)
if rest:
self.assert_open(rest[0], *rest[1:])
def assert_closed(self, sock, *rest):
if isinstance(sock, fd_types):
self.assert_fd_closed(sock)
else:
# Under Python3, the socket module returns -1 for a fileno
# of a closed socket; under Py2 it raises
self.assertEqual(sock.fileno(), -1)
self.assert_raises_EBADF(sock.getsockname)
self.assert_raises_EBADF(sock.accept)
if rest:
self.assert_closed(rest[0], *rest[1:])
def make_open_socket(self):
s = socket.socket()
try:
s.bind(DEFAULT_BIND_ADDR_TUPLE)
if WIN or greentest.LINUX:
# Windows and linux (with psutil) doesn't show as open until
# we call listen (linux with lsof accepts either)
s.listen(1)
self.assert_open(s, s.fileno())
except:
s.close()
s = None
raise
return s
# Sometimes its this one, sometimes it's test_ssl. No clue why or how.
@greentest.skipOnAppVeyor("This sometimes times out for no apparent reason.")
class TestSocket(Test):
def test_simple_close(self):
with Closing() as closer:
s = closer(self.make_open_socket())
fileno = s.fileno()
s.close()
self.assert_closed(s, fileno)
def test_makefile1(self):
with Closing() as closer:
s = closer(self.make_open_socket())
fileno = s.fileno()
f = closer(s.makefile())
self.assert_open(s, fileno)
# Under python 2, this closes socket wrapper object but not the file descriptor;
# under python 3, both stay open
s.close()
self.assert_open(s, fileno)
f.close()
self.assert_closed(s)
self.assert_closed(fileno)
def test_makefile2(self):
with Closing() as closer:
s = closer(self.make_open_socket())
fileno = s.fileno()
self.assert_open(s, fileno)
f = closer(s.makefile())
self.assert_open(s)
self.assert_open(s, fileno)
f.close()
# closing fileobject does not close the socket
self.assert_open(s, fileno)
s.close()
self.assert_closed(s, fileno)
def test_server_simple(self):
with Closing() as closer:
listener = closer(tcp_listener(backlog=1))
port = listener.getsockname()[1]
connector = closer(socket.socket())
def connect():
connector.connect((DEFAULT_CONNECT, port))
closer.running_task(threading.Thread(target=connect))
client_socket = closer.accept(listener)
fileno = client_socket.fileno()
self.assert_open(client_socket, fileno)
client_socket.close()
self.assert_closed(client_socket)
def test_server_makefile1(self):
with Closing() as closer:
listener = closer(tcp_listener(backlog=1))
port = listener.getsockname()[1]
connector = closer(socket.socket())
def connect():
connector.connect((DEFAULT_CONNECT, port))
closer.running_task(threading.Thread(target=connect))
client_socket = closer.accept(listener)
fileno = client_socket.fileno()
f = closer(client_socket.makefile())
self.assert_open(client_socket, fileno)
client_socket.close()
# Under python 2, this closes socket wrapper object but not the file descriptor;
# under python 3, both stay open
self.assert_open(client_socket, fileno)
f.close()
self.assert_closed(client_socket, fileno)
def test_server_makefile2(self):
with Closing() as closer:
listener = closer(tcp_listener(backlog=1))
port = listener.getsockname()[1]
connector = closer(socket.socket())
def connect():
connector.connect((DEFAULT_CONNECT, port))
closer.running_task(threading.Thread(target=connect))
client_socket = closer.accept(listener)
fileno = client_socket.fileno()
f = closer(client_socket.makefile())
self.assert_open(client_socket, fileno)
# closing fileobject does not close the socket
f.close()
self.assert_open(client_socket, fileno)
client_socket.close()
self.assert_closed(client_socket, fileno)
@greentest.skipOnAppVeyor("This sometimes times out for no apparent reason.")
class TestSSL(Test):
def _ssl_connect_task(self, connector, port, accepted_event):
connector.connect((DEFAULT_CONNECT, port))
try:
# Note: We get ResourceWarning about 'x'
# on Python 3 if we don't join the spawned thread
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
x = ctx.wrap_socket(connector)
# Wait to be fully accepted. We could otherwise raise ahead
# of the server and close ourself before it's ready to read.
accepted_event.wait()
except socket.error:
# Observed on Windows with PyPy2 5.9.0 and libuv:
# if we don't switch in a timely enough fashion,
# the server side runs ahead of us and closes
# our socket first, so this fails.
pass
else:
x.close()
def _make_ssl_connect_task(self, connector, port):
accepted_event = threading.Event()
t = threading.Thread(target=self._ssl_connect_task,
args=(connector, port, accepted_event))
t.daemon = True
t.accepted_event = accepted_event
return t
def test_simple_close(self):
with Closing() as closer:
s = closer(self.make_open_socket())
fileno = s.fileno()
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = closer(ctx.wrap_socket(s))
fileno = s.fileno()
self.assert_open(s, fileno)
s.close()
self.assert_closed(s, fileno)
def test_makefile1(self):
with Closing() as closer:
raw_s = closer(self.make_open_socket())
s = closer(ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER).wrap_socket(raw_s))
fileno = s.fileno()
self.assert_open(s, fileno)
f = closer(s.makefile())
self.assert_open(s, fileno)
s.close()
self.assert_open(s, fileno)
f.close()
raw_s.close()
self.assert_closed(s, fileno)
def test_makefile2(self):
with Closing() as closer:
s = closer(self.make_open_socket())
fileno = s.fileno()
s = closer(ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER).wrap_socket(s))
fileno = s.fileno()
self.assert_open(s, fileno)
f = closer(s.makefile())
self.assert_open(s, fileno)
f.close()
# closing fileobject does not close the socket
self.assert_open(s, fileno)
s.close()
self.assert_closed(s, fileno)
def _wrap_socket(self, sock, *, keyfile, certfile, server_side=False):
context = ssl.SSLContext(
ssl.PROTOCOL_TLS_SERVER
if server_side
else ssl.PROTOCOL_TLS_CLIENT
)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
context.load_cert_chain(certfile=certfile, keyfile=keyfile)
return context.wrap_socket(sock, server_side=server_side)
def test_server_simple(self):
with Closing() as closer:
listener = closer(tcp_listener(backlog=1))
port = listener.getsockname()[1]
connector = closer(socket.socket())
t = self._make_ssl_connect_task(connector, port)
closer.running_task(t)
client_socket = closer.accept(listener)
t.accepted_event.set()
client_socket = closer(
self._wrap_socket(client_socket, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True))
fileno = client_socket.fileno()
self.assert_open(client_socket, fileno)
client_socket.close()
self.assert_closed(client_socket, fileno)
def test_server_makefile1(self):
with Closing() as closer:
listener = closer(tcp_listener(backlog=1))
port = listener.getsockname()[1]
connector = closer(socket.socket())
t = self._make_ssl_connect_task(connector, port)
closer.running_task(t)
client_socket = closer.accept(listener)
t.accepted_event.set()
client_socket = closer(
self._wrap_socket(client_socket, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True))
fileno = client_socket.fileno()
self.assert_open(client_socket, fileno)
f = client_socket.makefile()
self.assert_open(client_socket, fileno)
client_socket.close()
self.assert_open(client_socket, fileno)
f.close()
self.assert_closed(client_socket, fileno)
def test_server_makefile2(self):
with Closing() as closer:
listener = closer(tcp_listener(backlog=1))
port = listener.getsockname()[1]
connector = closer(socket.socket())
t = self._make_ssl_connect_task(connector, port)
closer.running_task(t)
t.accepted_event.set()
client_socket = closer.accept(listener)
client_socket = closer(
self._wrap_socket(client_socket, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True))
fileno = client_socket.fileno()
self.assert_open(client_socket, fileno)
f = client_socket.makefile()
self.assert_open(client_socket, fileno)
# Closing fileobject does not close SSLObject
f.close()
self.assert_open(client_socket, fileno)
client_socket.close()
self.assert_closed(client_socket, fileno)
def test_serverssl_makefile1(self):
raw_listener = tcp_listener(backlog=1)
fileno = raw_listener.fileno()
port = raw_listener.getsockname()[1]
listener = self._wrap_socket(raw_listener, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True)
connector = socket.socket()
t = self._make_ssl_connect_task(connector, port)
t.start()
with CleaningUp(t, listener, raw_listener, connector) as client_socket:
t.accepted_event.set()
fileno = client_socket.fileno()
self.assert_open(client_socket, fileno)
f = client_socket.makefile()
self.assert_open(client_socket, fileno)
client_socket.close()
self.assert_open(client_socket, fileno)
f.close()
self.assert_closed(client_socket, fileno)
def test_serverssl_makefile2(self):
raw_listener = tcp_listener(backlog=1)
port = raw_listener.getsockname()[1]
listener = self._wrap_socket(raw_listener, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True)
accepted_event = threading.Event()
read_event = threading.Event()
def connect(connector=socket.socket()):
try:
connector.connect((DEFAULT_CONNECT, port))
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(connector)
accepted_event.wait()
s.sendall(b'test_serverssl_makefile2')
s.shutdown(socket.SHUT_RDWR)
read_event.wait()
s.close()
finally:
connector.close()
t = threading.Thread(target=connect)
t.daemon = True
t.start()
client_socket = None
with CleaningUp(t, listener, raw_listener) as client_socket:
accepted_event.set()
fileno = client_socket.fileno()
self.assert_open(client_socket, fileno)
f = client_socket.makefile()
self.assert_open(client_socket, fileno)
self.assertEqual(f.read(), 'test_serverssl_makefile2')
# Closing the other end of the socket
# before we finish reading can result in us
# getting EPIPE from OpenSSL 3.5 on certain
# systems, notably the manylinux images on Github
# actions. This hasn't shown up anywhere else.
read_event.set()
self.assertEqual(f.read(), '')
# Closing file object does not close the socket.
f.close()
if WIN and psutil:
# Hmm?
self.extra_allowed_open_states = (psutil.CONN_CLOSE_WAIT,)
self.assert_open(client_socket, fileno)
client_socket.close()
self.assert_closed(client_socket, fileno)
class Closing(object):
def __init__(self, *init):
self._objects = []
for i in init:
self.closing(i)
self.task = None
def accept(self, listener):
client_socket, _addr = listener.accept()
return self.closing(client_socket)
def __enter__(self):
o = self.objects()
if len(o) == 1:
return o[0]
return self
def objects(self):
# PyPy returns an object without __len__...
return list(reversed(self._objects))
def closing(self, o):
self._objects.append(o)
return o
__call__ = closing
def running_task(self, thread):
assert self.task is None
self.task = thread
self.task.start()
return self.task
def __exit__(self, t, v, tb):
# workaround for test_server_makefile1, test_server_makefile2,
# test_server_simple, test_serverssl_makefile1.
# On PyPy on Linux, it is important to join the SSL Connect
# Task FIRST, before closing the sockets. If we do it after
# (which makes more sense) we hang. It's not clear why, except
# that it has something to do with context switches. Inserting a call to
# gevent.sleep(0.1) instead of joining the task has the same
# effect. If the previous tests hang, then later tests can fail with
# SSLError: unknown alert type.
# XXX: Why do those two things happen?
# On PyPy on macOS, we don't have that problem and can use the
# more logical order.
try:
if self.task is not None:
self.task.join()
finally:
self.task = None
for o in self.objects():
try:
o.close()
except Exception: # pylint:disable=broad-except
pass
self._objects = ()
class CleaningUp(Closing):
def __init__(self, task, listener, *other_sockets):
super(CleaningUp, self).__init__(listener, *other_sockets)
self.task = task
self.listener = listener
def __enter__(self):
return self.accept(self.listener)
def __exit__(self, t, v, tb):
try:
Closing.__exit__(self, t, v, tb)
finally:
self.listener = None
if __name__ == '__main__':
greentest.main()
@@ -0,0 +1,47 @@
import sys
import unittest
from gevent.testing import TestCase
import gevent
from gevent.timeout import Timeout
@unittest.skipUnless(
hasattr(sys, 'gettotalrefcount'),
"Needs debug build"
)
# XXX: This name makes no sense. What was this for originally?
class TestQueue(TestCase): # pragma: no cover
# pylint:disable=bare-except,no-member
def test(self):
refcounts = []
for _ in range(15):
try:
Timeout.start_new(0.01)
gevent.sleep(0.1)
self.fail('must raise Timeout')
except Timeout:
pass
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(
final, previous,
"total refcount mismatch: %s" % refcounts)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,165 @@
from gevent import monkey
monkey.patch_all()
import sys
import unittest
from gevent.testing.testcase import SubscriberCleanupMixin
class TestMonkey(SubscriberCleanupMixin, unittest.TestCase):
maxDiff = None
def setUp(self):
super(TestMonkey, self).setUp()
self.all_events = []
self.addSubscriber(self.all_events.append)
self.orig_saved = orig_saved = {}
for k, v in monkey.saved.items():
orig_saved[k] = v.copy()
def tearDown(self):
monkey.saved = self.orig_saved
del self.orig_saved
del self.all_events
super(TestMonkey, self).tearDown()
def test_time(self):
import time
from gevent import time as gtime
self.assertIs(time.sleep, gtime.sleep)
def test_thread(self):
import _thread as thread
import threading
from gevent import thread as gthread
self.assertIs(thread.start_new_thread, gthread.start_new_thread)
if sys.version_info[:2] < (3, 13):
self.assertIs(threading._start_new_thread, gthread.start_new_thread)
else:
self.assertIs(threading._start_joinable_thread, gthread.start_joinable_thread)
# Event patched by default
self.assertTrue(monkey.is_object_patched('threading', 'Event'))
if sys.version_info[0] == 2:
from gevent import threading as gthreading
from gevent.event import Event as GEvent
self.assertIs(threading._sleep, gthreading._sleep)
self.assertTrue(monkey.is_object_patched('threading', '_Event'))
self.assertIs(threading._Event, GEvent)
def test_socket(self):
import socket
from gevent import socket as gevent_socket
self.assertIs(socket.create_connection, gevent_socket.create_connection)
def test_os(self):
import os
import types
from gevent import os as gos
for name in ('fork', 'forkpty'):
if hasattr(os, name):
attr = getattr(os, name)
self.assertNotIn('built-in', repr(attr))
self.assertNotIsInstance(attr, types.BuiltinFunctionType)
self.assertIsInstance(attr, types.FunctionType)
self.assertIs(attr, getattr(gos, name))
def test_saved(self):
self.assertTrue(monkey.saved)
for modname, objects in monkey.saved.items():
self.assertTrue(monkey.is_module_patched(modname))
for objname in objects:
self.assertTrue(monkey.is_object_patched(modname, objname))
def test_patch_subprocess_twice(self):
Popen = monkey.get_original('subprocess', 'Popen')
self.assertNotIn('gevent', repr(Popen))
self.assertIs(Popen, monkey.get_original('subprocess', 'Popen'))
monkey.patch_subprocess()
self.assertIs(Popen, monkey.get_original('subprocess', 'Popen'))
def test_patch_twice_warnings_events(self):
import warnings
all_events = self.all_events
with warnings.catch_warnings(record=True) as issued_warnings:
# Patch again, triggering just one warning, for
# a different set of arguments. Because we're going to False instead of
# turning something on, nothing is actually done, no events are issued.
monkey.patch_all(os=False, extra_kwarg=42)
self.assertEqual(len(issued_warnings), 1)
self.assertIn('more than once', str(issued_warnings[0].message))
self.assertEqual(all_events, [])
# Same warning again, but still nothing is done.
del issued_warnings[:]
monkey.patch_all(os=False)
self.assertEqual(len(issued_warnings), 1)
self.assertIn('more than once', str(issued_warnings[0].message))
self.assertEqual(all_events, [])
self.orig_saved['_gevent_saved_patch_all_module_settings'] = monkey.saved[
'_gevent_saved_patch_all_module_settings']
# Make sure that re-patching did not change the monkey.saved
# attribute, overwriting the original functions.
if 'logging' in monkey.saved and 'logging' not in self.orig_saved:
# some part of the warning or unittest machinery imports logging
self.orig_saved['logging'] = monkey.saved['logging']
self.assertEqual(self.orig_saved, monkey.saved)
# Make sure some problematic attributes stayed correct.
# NOTE: This was only a problem if threading was not previously imported.
for k, v in monkey.saved['threading'].items():
self.assertNotIn('gevent', str(v), (k, v))
def test_patch_events(self):
from gevent import events
from gevent.testing import verify
all_events = self.all_events
def veto(event):
if isinstance(event, events.GeventWillPatchModuleEvent) and event.module_name == 'ssl':
raise events.DoNotPatch
self.addSubscriber(veto)
monkey.saved = {} # Reset
monkey.patch_all(thread=False, select=False, extra_kwarg=42) # Go again
self.assertIsInstance(all_events[0], events.GeventWillPatchAllEvent)
self.assertEqual({'extra_kwarg': 42}, all_events[0].patch_all_kwargs)
verify.verifyObject(events.IGeventWillPatchAllEvent, all_events[0])
self.assertIsInstance(all_events[1], events.GeventWillPatchModuleEvent)
verify.verifyObject(events.IGeventWillPatchModuleEvent, all_events[1])
self.assertIsInstance(all_events[2], events.GeventDidPatchModuleEvent)
verify.verifyObject(events.IGeventWillPatchModuleEvent, all_events[1])
self.assertIsInstance(all_events[-2], events.GeventDidPatchBuiltinModulesEvent)
verify.verifyObject(events.IGeventDidPatchBuiltinModulesEvent, all_events[-2])
self.assertIsInstance(all_events[-1], events.GeventDidPatchAllEvent)
verify.verifyObject(events.IGeventDidPatchAllEvent, all_events[-1])
for e in all_events:
self.assertFalse(isinstance(e, events.GeventDidPatchModuleEvent)
and e.module_name == 'ssl')
def test_patch_queue(self):
import queue
import gevent.queue as gq
# pylint:disable=no-member
self.assertIs(queue.SimpleQueue, gq.SimpleQueue)
self.assertIs(queue.LifoQueue, gq.LifoQueue)
self.assertIs(queue.Queue, gq.Queue)
self.assertIs(queue.PriorityQueue, gq.PriorityQueue)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,16 @@
# Under Python 2, if the `future` module is installed, we get
# a `builtins` module, which mimics the `builtins` module from
# Python 3, but does not have the __import__ and some other functions.
# Make sure we can still run in that case.
import sys
try:
# fake out a "broken" builtins module
import builtins
except ImportError:
class builtins(object):
pass
sys.modules['builtins'] = builtins()
if not hasattr(builtins, '__import__'):
import gevent.monkey
gevent.monkey.patch_builtins()
@@ -0,0 +1,28 @@
from gevent.monkey import patch_all
patch_all(thread=False)
from threading import Thread
import time
# The first time we init the hub is in the native
# thread with time.sleep(), needing multiple
# threads at the same time. Note: this is very timing
# dependent.
# See #687
def func():
time.sleep()
def main():
threads = []
for _ in range(3):
th = Thread(target=func)
th.start()
threads.append(th)
for th in threads:
th.join()
if __name__ == '__main__':
main()

Some files were not shown because too many files have changed in this diff Show More