Skip to content

Commit 2ea4583

Browse files
committed
ENH: Add test decorator 'with_connectivity_check'.
Allows tests to check that a url is available before bubbling up an error from a test case. TST: Change tests to use with_connectivity_check to better approximate previous behavior TST: Add check_before_test option to with_connectivity_check. CLN: PEP8 all the recent jratner code
1 parent 68fc014 commit 2ea4583

File tree

5 files changed

+112
-23
lines changed

5 files changed

+112
-23
lines changed

pandas/io/tests/test_fred.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pandas.io.data as web
99
from pandas.util.testing import (network, assert_frame_equal,
1010
assert_series_equal,
11-
assert_almost_equal)
11+
assert_almost_equal, with_connectivity_check)
1212
from numpy.testing.decorators import slow
1313

1414
import urllib2
@@ -17,7 +17,7 @@
1717
class TestFred(unittest.TestCase):
1818

1919
@slow
20-
@network
20+
@with_connectivity_check("http://www.google.com")
2121
def test_fred(self):
2222
"""
2323
Throws an exception when DataReader can't get a 200 response from

pandas/io/tests/test_ga.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import nose
66
import pandas as pd
77
from pandas import DataFrame
8-
from pandas.util.testing import network, assert_frame_equal
8+
from pandas.util.testing import network, assert_frame_equal, with_connectivity_check
99
from numpy.testing.decorators import slow
1010

1111
try:
@@ -71,7 +71,7 @@ def test_getdata(self):
7171
raise nose.SkipTest
7272

7373
@slow
74-
@network
74+
@with_connectivity_check("http://www.google.com")
7575
def test_iterator(self):
7676
try:
7777
reader = GAnalytics()
@@ -97,16 +97,9 @@ def test_iterator(self):
9797

9898
except AuthenticationConfigError:
9999
raise nose.SkipTest
100-
except httplib2.ServerNotFoundError:
101-
try:
102-
h = httplib2.Http()
103-
response, content = h.request("http://www.google.com")
104-
raise
105-
except httplib2.ServerNotFoundError:
106-
raise nose.SkipTest
107100

108101
@slow
109-
@network
102+
@with_connectivity_check("http://www.google.com")
110103
def test_segment(self):
111104
try:
112105
end_date = datetime.now()

pandas/io/tests/test_google.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@
22
import nose
33
from datetime import datetime
44

5+
import numpy as np
56
import pandas as pd
67
import pandas.io.data as web
7-
from pandas.util.testing import (network, assert_series_equal)
8-
from numpy.testing.decorators import slow
9-
import numpy as np
10-
11-
import urllib2
8+
from pandas.util.testing import network, with_connectivity_check
129

1310

1411
class TestGoogle(unittest.TestCase):
1512

16-
@with_connectivity_check('http://www.google.com')
13+
@with_connectivity_check("http://www.google.com")
1714
def test_google(self):
1815
# asserts that google is minimally working and that it throws
1916
# an exception when DataReader can't get a 200 response from

pandas/io/tests/test_yahoo.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55
import pandas as pd
66
import pandas.io.data as web
7-
from pandas.util.testing import network, assert_series_equal
7+
from pandas.util.testing import network, assert_series_equal, with_connectivity_check
88

99

1010
class TestYahoo(unittest.TestCase):
1111

12-
@network
12+
@with_connectivity_check("http://www.google.com")
1313
def test_yahoo(self):
1414
# asserts that yahoo is minimally working and that it throws
15-
# an excecption when DataReader can't get a 200 response from
15+
# an exception when DataReader can't get a 200 response from
1616
# yahoo
1717
start = datetime(2010, 1, 1)
1818
end = datetime(2013, 01, 27)

pandas/util/testing.py

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
K = 4
4242
_RAISE_NETWORK_ERROR_DEFAULT = False
4343

44+
4445
def rands(n):
4546
choices = string.ascii_letters + string.digits
4647
return ''.join(random.choice(choices) for _ in xrange(n))
@@ -675,22 +676,26 @@ def optional_args(decorator):
675676
def function(): pass
676677
677678
Calls decorator with decorator(f, *args, **kwargs)"""
679+
678680
@wraps(decorator)
679681
def wrapper(*args, **kwargs):
680682
def dec(f):
681683
return decorator(f, *args, **kwargs)
684+
682685
is_decorating = not kwargs and len(args) == 1 and callable(args[0])
683686
if is_decorating:
684687
f = args[0]
685688
args = []
686689
return dec(f)
687690
else:
688691
return dec
692+
689693
return wrapper
690694

695+
691696
@optional_args
692697
def network(t, raise_on_error=_RAISE_NETWORK_ERROR_DEFAULT,
693-
error_classes=(IOError,)):
698+
error_classes=(IOError,)):
694699
"""
695700
Label a test as requiring network connection and skip test if it encounters a ``URLError``.
696701
@@ -751,6 +756,7 @@ def network(t, raise_on_error=_RAISE_NETWORK_ERROR_DEFAULT,
751756
``pandas/util/testing.py`` sets the default behavior (currently False).
752757
"""
753758
t.network = True
759+
754760
@wraps(t)
755761
def network_wrapper(*args, **kwargs):
756762
if raise_on_error:
@@ -760,8 +766,98 @@ def network_wrapper(*args, **kwargs):
760766
return t(*args, **kwargs)
761767
except error_classes as e:
762768
raise nose.SkipTest("Skipping test %s" % e)
769+
763770
return network_wrapper
764771

772+
773+
def can_connect(url):
774+
"""tries to connect to the given url. True if succeeds, False if IOError raised"""
775+
try:
776+
urllib2.urlopen(url)
777+
except IOError:
778+
return False
779+
else:
780+
return True
781+
782+
783+
@optional_args
784+
def with_connectivity_check(t, url="http://www.google.com",
785+
raise_on_error=_RAISE_NETWORK_ERROR_DEFAULT, check_before_test=False,
786+
error_classes=IOError):
787+
"""
788+
Label a test as requiring network connection and, if an error is
789+
encountered, only raise if it does not find a network connection.
790+
791+
In comparison to ``network``, this assumes an added contract to your test:
792+
you must assert that, under normal conditions, your test will ONLY fail if
793+
it does not have network connectivity.
794+
795+
You can call this in 3 ways: as a standard decorator, with keyword
796+
arguments, or with a positional argument that is the url to check.
797+
798+
Parameters
799+
----------
800+
t : callable
801+
The test requiring network connectivity.
802+
url : path
803+
The url to test via ``urllib2.urlopen`` to check for connectivity.
804+
Defaults to 'http://www.google.com'.
805+
raise_on_error : bool
806+
If True, never catches errors.
807+
check_before_test : bool
808+
If True, checks connectivity before running the test case.
809+
error_classes : tuple or Exception
810+
error classes to ignore. If not in ``error_classes``, raises the error.
811+
defaults to IOError. Be careful about changing the error classes here.
812+
813+
NOTE: ``raise_on_error`` supercedes ``check_before_test``
814+
Returns
815+
-------
816+
t : callable
817+
The decorated test ``t``, with checks for connectivity errors.
818+
819+
Example
820+
-------
821+
822+
In this example, you see how it will raise the error if it can connect to
823+
the url::
824+
>>> @with_connectivity_check("http://www.yahoo.com")
825+
... def test_something_with_yahoo():
826+
... raise IOError("Failure Message")
827+
>>> test_something_with_yahoo()
828+
Traceback (most recent call last):
829+
...
830+
IOError: Failure Message
831+
832+
I you set check_before_test, it will check the url first and not run the test on failure::
833+
>>> @with_connectivity_check("failing://url.blaher", check_before_test=True)
834+
... def test_something():
835+
... print("I ran!")
836+
... raise ValueError("Failure")
837+
>>> test_something()
838+
Traceback (most recent call last):
839+
...
840+
SkipTest
841+
"""
842+
t.network = True
843+
844+
@wraps(t)
845+
def wrapper(*args, **kwargs):
846+
if check_before_test and not raise_on_error:
847+
if not can_connect(url):
848+
raise nose.SkipTest
849+
try:
850+
return t(*args, **kwargs)
851+
except error_classes as e:
852+
if raise_on_error or can_connect(url):
853+
raise
854+
else:
855+
raise nose.SkipTest("Skipping test due to lack of connectivity"
856+
" and error %s" % e)
857+
858+
return wrapper
859+
860+
765861
class SimpleMock(object):
766862
"""
767863
Poor man's mocking object
@@ -806,11 +902,13 @@ def stdin_encoding(encoding=None):
806902
807903
"""
808904
import sys
905+
809906
_stdin = sys.stdin
810907
sys.stdin = SimpleMock(sys.stdin, "encoding", encoding)
811908
yield
812909
sys.stdin = _stdin
813910

911+
814912
def assertRaisesRegexp(exception, regexp, callable, *args, **kwargs):
815913
""" Port of assertRaisesRegexp from unittest in Python 2.7 - used in with statement.
816914
@@ -842,6 +940,7 @@ def assertRaisesRegexp(exception, regexp, callable, *args, **kwargs):
842940
"""
843941

844942
import re
943+
845944
try:
846945
callable(*args, **kwargs)
847946
except Exception as e:
@@ -855,7 +954,7 @@ def assertRaisesRegexp(exception, regexp, callable, *args, **kwargs):
855954
expected_regexp = re.compile(regexp)
856955
if not expected_regexp.search(str(e)):
857956
raise AssertionError('"%s" does not match "%s"' %
858-
(expected_regexp.pattern, str(e)))
957+
(expected_regexp.pattern, str(e)))
859958
else:
860959
# Apparently some exceptions don't have a __name__ attribute? Just aping unittest library here
861960
name = getattr(exception, "__name__", str(exception))

0 commit comments

Comments
 (0)