2
2
3
3
import functools
4
4
from itertools import product
5
+ from distutils .version import LooseVersion
5
6
6
7
import nose
7
8
from nose .tools import assert_raises , assert_true , assert_false , assert_equal
17
18
from pandas .util .testing import makeCustomDataframe as mkdf
18
19
19
20
from pandas .computation import pytables
20
- from pandas .computation .expressions import _USE_NUMEXPR
21
21
from pandas .computation .engines import _engines
22
22
from pandas .computation .expr import PythonExprVisitor , PandasExprVisitor
23
- from pandas .computation .ops import (_binary_ops_dict , _unary_ops_dict ,
23
+ from pandas .computation .ops import (_binary_ops_dict ,
24
24
_special_case_arith_ops_syms ,
25
25
_arith_ops_syms , _bool_ops_syms )
26
26
from pandas .computation .common import NameResolutionError
27
+
27
28
import pandas .computation .expr as expr
28
29
import pandas .util .testing as tm
29
30
from pandas .util .testing import (assert_frame_equal , randbool ,
35
36
_scalar_skip = 'in' , 'not in'
36
37
37
38
38
- def skip_if_no_ne (engine = 'numexpr' ):
39
- if not _USE_NUMEXPR and engine == 'numexpr' :
40
- raise nose .SkipTest ("numexpr engine not installed or disabled" )
41
-
42
-
43
39
def engine_has_neg_frac (engine ):
44
40
return _engines [engine ].has_neg_frac
45
41
@@ -108,7 +104,7 @@ class TestEvalNumexprPandas(tm.TestCase):
108
104
@classmethod
109
105
def setUpClass (cls ):
110
106
super (TestEvalNumexprPandas , cls ).setUpClass ()
111
- skip_if_no_ne ()
107
+ tm . skip_if_no_ne ()
112
108
import numexpr as ne
113
109
cls .ne = ne
114
110
cls .engine = 'numexpr'
@@ -426,7 +422,7 @@ def check_single_invert_op(self, lhs, cmp1, rhs):
426
422
assert_array_equal (expected , result )
427
423
428
424
for engine in self .current_engines :
429
- skip_if_no_ne (engine )
425
+ tm . skip_if_no_ne (engine )
430
426
assert_array_equal (result , pd .eval ('~elb' , engine = engine ,
431
427
parser = self .parser ))
432
428
@@ -457,7 +453,7 @@ def check_compound_invert_op(self, lhs, cmp1, rhs):
457
453
458
454
# make sure the other engines work the same as this one
459
455
for engine in self .current_engines :
460
- skip_if_no_ne (engine )
456
+ tm . skip_if_no_ne (engine )
461
457
ev = pd .eval (ex , engine = self .engine , parser = self .parser )
462
458
assert_array_equal (ev , result )
463
459
@@ -709,7 +705,7 @@ class TestEvalNumexprPython(TestEvalNumexprPandas):
709
705
@classmethod
710
706
def setUpClass (cls ):
711
707
super (TestEvalNumexprPython , cls ).setUpClass ()
712
- skip_if_no_ne ()
708
+ tm . skip_if_no_ne ()
713
709
import numexpr as ne
714
710
cls .ne = ne
715
711
cls .engine = 'numexpr'
@@ -788,7 +784,7 @@ class TestAlignment(object):
788
784
lhs_index_types = index_types + ('s' ,) # 'p'
789
785
790
786
def check_align_nested_unary_op (self , engine , parser ):
791
- skip_if_no_ne (engine )
787
+ tm . skip_if_no_ne (engine )
792
788
s = 'df * ~2'
793
789
df = mkdf (5 , 3 , data_gen_f = f )
794
790
res = pd .eval (s , engine = engine , parser = parser )
@@ -799,7 +795,7 @@ def test_align_nested_unary_op(self):
799
795
yield self .check_align_nested_unary_op , engine , parser
800
796
801
797
def check_basic_frame_alignment (self , engine , parser ):
802
- skip_if_no_ne (engine )
798
+ tm . skip_if_no_ne (engine )
803
799
args = product (self .lhs_index_types , self .index_types ,
804
800
self .index_types )
805
801
for lr_idx_type , rr_idx_type , c_idx_type in args :
@@ -815,7 +811,7 @@ def test_basic_frame_alignment(self):
815
811
yield self .check_basic_frame_alignment , engine , parser
816
812
817
813
def check_frame_comparison (self , engine , parser ):
818
- skip_if_no_ne (engine )
814
+ tm . skip_if_no_ne (engine )
819
815
args = product (self .lhs_index_types , repeat = 2 )
820
816
for r_idx_type , c_idx_type in args :
821
817
df = mkdf (10 , 10 , data_gen_f = f , r_idx_type = r_idx_type ,
@@ -833,7 +829,7 @@ def test_frame_comparison(self):
833
829
yield self .check_frame_comparison , engine , parser
834
830
835
831
def check_medium_complex_frame_alignment (self , engine , parser ):
836
- skip_if_no_ne (engine )
832
+ tm . skip_if_no_ne (engine )
837
833
args = product (self .lhs_index_types , self .index_types ,
838
834
self .index_types , self .index_types )
839
835
@@ -850,7 +846,7 @@ def test_medium_complex_frame_alignment(self):
850
846
yield self .check_medium_complex_frame_alignment , engine , parser
851
847
852
848
def check_basic_frame_series_alignment (self , engine , parser ):
853
- skip_if_no_ne (engine )
849
+ tm . skip_if_no_ne (engine )
854
850
855
851
def testit (r_idx_type , c_idx_type , index_name ):
856
852
df = mkdf (10 , 10 , data_gen_f = f , r_idx_type = r_idx_type ,
@@ -878,7 +874,7 @@ def test_basic_frame_series_alignment(self):
878
874
yield self .check_basic_frame_series_alignment , engine , parser
879
875
880
876
def check_basic_series_frame_alignment (self , engine , parser ):
881
- skip_if_no_ne (engine )
877
+ tm . skip_if_no_ne (engine )
882
878
883
879
def testit (r_idx_type , c_idx_type , index_name ):
884
880
df = mkdf (10 , 7 , data_gen_f = f , r_idx_type = r_idx_type ,
@@ -911,7 +907,7 @@ def test_basic_series_frame_alignment(self):
911
907
yield self .check_basic_series_frame_alignment , engine , parser
912
908
913
909
def check_series_frame_commutativity (self , engine , parser ):
914
- skip_if_no_ne (engine )
910
+ tm . skip_if_no_ne (engine )
915
911
args = product (self .lhs_index_types , self .index_types , ('+' , '*' ),
916
912
('index' , 'columns' ))
917
913
for r_idx_type , c_idx_type , op , index_name in args :
@@ -934,7 +930,7 @@ def test_series_frame_commutativity(self):
934
930
yield self .check_series_frame_commutativity , engine , parser
935
931
936
932
def check_complex_series_frame_alignment (self , engine , parser ):
937
- skip_if_no_ne (engine )
933
+ tm . skip_if_no_ne (engine )
938
934
939
935
import random
940
936
args = product (self .lhs_index_types , self .index_types ,
@@ -978,7 +974,7 @@ def test_complex_series_frame_alignment(self):
978
974
yield self .check_complex_series_frame_alignment , engine , parser
979
975
980
976
def check_performance_warning_for_poor_alignment (self , engine , parser ):
981
- skip_if_no_ne (engine )
977
+ tm . skip_if_no_ne (engine )
982
978
df = DataFrame (randn (1000 , 10 ))
983
979
s = Series (randn (10000 ))
984
980
if engine == 'numexpr' :
@@ -1034,7 +1030,7 @@ class TestOperationsNumExprPandas(tm.TestCase):
1034
1030
@classmethod
1035
1031
def setUpClass (cls ):
1036
1032
super (TestOperationsNumExprPandas , cls ).setUpClass ()
1037
- skip_if_no_ne ()
1033
+ tm . skip_if_no_ne ()
1038
1034
cls .engine = 'numexpr'
1039
1035
cls .parser = 'pandas'
1040
1036
cls .arith_ops = expr ._arith_ops_syms + expr ._cmp_ops_syms
@@ -1194,7 +1190,7 @@ def test_assignment_fails(self):
1194
1190
local_dict = {'df' : df , 'df2' : df2 })
1195
1191
1196
1192
def test_assignment_column (self ):
1197
- skip_if_no_ne ('numexpr' )
1193
+ tm . skip_if_no_ne ('numexpr' )
1198
1194
df = DataFrame (np .random .randn (5 , 2 ), columns = list ('ab' ))
1199
1195
orig_df = df .copy ()
1200
1196
@@ -1345,10 +1341,9 @@ class TestOperationsNumExprPython(TestOperationsNumExprPandas):
1345
1341
@classmethod
1346
1342
def setUpClass (cls ):
1347
1343
super (TestOperationsNumExprPython , cls ).setUpClass ()
1348
- if not _USE_NUMEXPR :
1349
- raise nose .SkipTest ("numexpr engine not installed" )
1350
1344
cls .engine = 'numexpr'
1351
1345
cls .parser = 'python'
1346
+ tm .skip_if_no_ne (cls .engine )
1352
1347
cls .arith_ops = expr ._arith_ops_syms + expr ._cmp_ops_syms
1353
1348
cls .arith_ops = filter (lambda x : x not in ('in' , 'not in' ),
1354
1349
cls .arith_ops )
@@ -1435,7 +1430,7 @@ def setUpClass(cls):
1435
1430
class TestScope (object ):
1436
1431
1437
1432
def check_global_scope (self , e , engine , parser ):
1438
- skip_if_no_ne (engine )
1433
+ tm . skip_if_no_ne (engine )
1439
1434
assert_array_equal (_var_s * 2 , pd .eval (e , engine = engine ,
1440
1435
parser = parser ))
1441
1436
@@ -1445,7 +1440,7 @@ def test_global_scope(self):
1445
1440
yield self .check_global_scope , e , engine , parser
1446
1441
1447
1442
def check_no_new_locals (self , engine , parser ):
1448
- skip_if_no_ne (engine )
1443
+ tm . skip_if_no_ne (engine )
1449
1444
x = 1
1450
1445
lcls = locals ().copy ()
1451
1446
pd .eval ('x + 1' , local_dict = lcls , engine = engine , parser = parser )
@@ -1458,7 +1453,7 @@ def test_no_new_locals(self):
1458
1453
yield self .check_no_new_locals , engine , parser
1459
1454
1460
1455
def check_no_new_globals (self , engine , parser ):
1461
- skip_if_no_ne (engine )
1456
+ tm . skip_if_no_ne (engine )
1462
1457
x = 1
1463
1458
gbls = globals ().copy ()
1464
1459
pd .eval ('x + 1' , engine = engine , parser = parser )
@@ -1471,21 +1466,21 @@ def test_no_new_globals(self):
1471
1466
1472
1467
1473
1468
def test_invalid_engine ():
1474
- skip_if_no_ne ()
1469
+ tm . skip_if_no_ne ()
1475
1470
assertRaisesRegexp (KeyError , 'Invalid engine \' asdf\' passed' ,
1476
1471
pd .eval , 'x + y' , local_dict = {'x' : 1 , 'y' : 2 },
1477
1472
engine = 'asdf' )
1478
1473
1479
1474
1480
1475
def test_invalid_parser ():
1481
- skip_if_no_ne ()
1476
+ tm . skip_if_no_ne ()
1482
1477
assertRaisesRegexp (KeyError , 'Invalid parser \' asdf\' passed' ,
1483
1478
pd .eval , 'x + y' , local_dict = {'x' : 1 , 'y' : 2 },
1484
1479
parser = 'asdf' )
1485
1480
1486
1481
1487
1482
def check_is_expr_syntax (engine ):
1488
- skip_if_no_ne (engine )
1483
+ tm . skip_if_no_ne (engine )
1489
1484
s = 1
1490
1485
valid1 = 's + 1'
1491
1486
valid2 = '__y + _xx'
@@ -1494,7 +1489,7 @@ def check_is_expr_syntax(engine):
1494
1489
1495
1490
1496
1491
def check_is_expr_names (engine ):
1497
- skip_if_no_ne (engine )
1492
+ tm . skip_if_no_ne (engine )
1498
1493
r , s = 1 , 2
1499
1494
valid = 's + r'
1500
1495
invalid = '__y + __x'
@@ -1517,7 +1512,7 @@ def test_is_expr_names():
1517
1512
1518
1513
1519
1514
def check_disallowed_nodes (engine , parser ):
1520
- skip_if_no_ne (engine )
1515
+ tm . skip_if_no_ne (engine )
1521
1516
VisitorClass = _parsers [parser ]
1522
1517
uns_ops = VisitorClass .unsupported_nodes
1523
1518
inst = VisitorClass ('x + 1' , engine , parser )
@@ -1532,7 +1527,7 @@ def test_disallowed_nodes():
1532
1527
1533
1528
1534
1529
def check_syntax_error_exprs (engine , parser ):
1535
- skip_if_no_ne (engine )
1530
+ tm . skip_if_no_ne (engine )
1536
1531
e = 's +'
1537
1532
assert_raises (SyntaxError , pd .eval , e , engine = engine , parser = parser )
1538
1533
@@ -1543,7 +1538,7 @@ def test_syntax_error_exprs():
1543
1538
1544
1539
1545
1540
def check_name_error_exprs (engine , parser ):
1546
- skip_if_no_ne (engine )
1541
+ tm . skip_if_no_ne (engine )
1547
1542
e = 's + t'
1548
1543
assert_raises (NameError , pd .eval , e , engine = engine , parser = parser )
1549
1544
@@ -1553,6 +1548,33 @@ def test_name_error_exprs():
1553
1548
yield check_name_error_exprs , engine , parser
1554
1549
1555
1550
1551
+ def check_invalid_numexpr_version (engine , parser ):
1552
+ def testit ():
1553
+ a , b = 1 , 2
1554
+ res = pd .eval ('a + b' , engine = engine , parser = parser )
1555
+ tm .assert_equal (res , 3 )
1556
+
1557
+ if engine == 'numexpr' :
1558
+ try :
1559
+ import numexpr as ne
1560
+ except ImportError :
1561
+ raise nose .SkipTest ("no numexpr" )
1562
+ else :
1563
+ if ne .__version__ < LooseVersion ('2.0' ):
1564
+ with tm .assertRaisesRegexp (ImportError , "'numexpr' version is "
1565
+ ".+, must be >= 2.0" ):
1566
+ testit ()
1567
+ else :
1568
+ testit ()
1569
+ else :
1570
+ testit ()
1571
+
1572
+
1573
+ def test_invalid_numexpr_version ():
1574
+ for engine , parser in ENGINES_PARSERS :
1575
+ yield check_invalid_numexpr_version , engine , parser
1576
+
1577
+
1556
1578
if __name__ == '__main__' :
1557
1579
nose .runmodule (argv = [__file__ , '-vvs' , '-x' , '--pdb' , '--pdb-failure' ],
1558
1580
exit = False )
0 commit comments