fix secure tab
This commit is contained in:
@@ -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())
|
||||
+30
@@ -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
Reference in New Issue
Block a user