22
33import functools
44from itertools import product
5+ from distutils .version import LooseVersion
56
67import nose
78from nose .tools import assert_raises , assert_true , assert_false , assert_equal
1718from pandas .util .testing import makeCustomDataframe as mkdf
1819
1920from pandas .computation import pytables
20- from pandas .computation .expressions import _USE_NUMEXPR
2121from pandas .computation .engines import _engines
2222from 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 ,
2424 _special_case_arith_ops_syms ,
2525 _arith_ops_syms , _bool_ops_syms )
2626from pandas .computation .common import NameResolutionError
27+
2728import pandas .computation .expr as expr
2829import pandas .util .testing as tm
2930from pandas .util .testing import (assert_frame_equal , randbool ,
3536_scalar_skip = 'in' , 'not in'
3637
3738
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-
4339def engine_has_neg_frac (engine ):
4440 return _engines [engine ].has_neg_frac
4541
@@ -108,7 +104,7 @@ class TestEvalNumexprPandas(tm.TestCase):
108104 @classmethod
109105 def setUpClass (cls ):
110106 super (TestEvalNumexprPandas , cls ).setUpClass ()
111- skip_if_no_ne ()
107+ tm . skip_if_no_ne ()
112108 import numexpr as ne
113109 cls .ne = ne
114110 cls .engine = 'numexpr'
@@ -426,7 +422,7 @@ def check_single_invert_op(self, lhs, cmp1, rhs):
426422 assert_array_equal (expected , result )
427423
428424 for engine in self .current_engines :
429- skip_if_no_ne (engine )
425+ tm . skip_if_no_ne (engine )
430426 assert_array_equal (result , pd .eval ('~elb' , engine = engine ,
431427 parser = self .parser ))
432428
@@ -457,7 +453,7 @@ def check_compound_invert_op(self, lhs, cmp1, rhs):
457453
458454 # make sure the other engines work the same as this one
459455 for engine in self .current_engines :
460- skip_if_no_ne (engine )
456+ tm . skip_if_no_ne (engine )
461457 ev = pd .eval (ex , engine = self .engine , parser = self .parser )
462458 assert_array_equal (ev , result )
463459
@@ -709,7 +705,7 @@ class TestEvalNumexprPython(TestEvalNumexprPandas):
709705 @classmethod
710706 def setUpClass (cls ):
711707 super (TestEvalNumexprPython , cls ).setUpClass ()
712- skip_if_no_ne ()
708+ tm . skip_if_no_ne ()
713709 import numexpr as ne
714710 cls .ne = ne
715711 cls .engine = 'numexpr'
@@ -788,7 +784,7 @@ class TestAlignment(object):
788784 lhs_index_types = index_types + ('s' ,) # 'p'
789785
790786 def check_align_nested_unary_op (self , engine , parser ):
791- skip_if_no_ne (engine )
787+ tm . skip_if_no_ne (engine )
792788 s = 'df * ~2'
793789 df = mkdf (5 , 3 , data_gen_f = f )
794790 res = pd .eval (s , engine = engine , parser = parser )
@@ -799,7 +795,7 @@ def test_align_nested_unary_op(self):
799795 yield self .check_align_nested_unary_op , engine , parser
800796
801797 def check_basic_frame_alignment (self , engine , parser ):
802- skip_if_no_ne (engine )
798+ tm . skip_if_no_ne (engine )
803799 args = product (self .lhs_index_types , self .index_types ,
804800 self .index_types )
805801 for lr_idx_type , rr_idx_type , c_idx_type in args :
@@ -815,7 +811,7 @@ def test_basic_frame_alignment(self):
815811 yield self .check_basic_frame_alignment , engine , parser
816812
817813 def check_frame_comparison (self , engine , parser ):
818- skip_if_no_ne (engine )
814+ tm . skip_if_no_ne (engine )
819815 args = product (self .lhs_index_types , repeat = 2 )
820816 for r_idx_type , c_idx_type in args :
821817 df = mkdf (10 , 10 , data_gen_f = f , r_idx_type = r_idx_type ,
@@ -833,7 +829,7 @@ def test_frame_comparison(self):
833829 yield self .check_frame_comparison , engine , parser
834830
835831 def check_medium_complex_frame_alignment (self , engine , parser ):
836- skip_if_no_ne (engine )
832+ tm . skip_if_no_ne (engine )
837833 args = product (self .lhs_index_types , self .index_types ,
838834 self .index_types , self .index_types )
839835
@@ -850,7 +846,7 @@ def test_medium_complex_frame_alignment(self):
850846 yield self .check_medium_complex_frame_alignment , engine , parser
851847
852848 def check_basic_frame_series_alignment (self , engine , parser ):
853- skip_if_no_ne (engine )
849+ tm . skip_if_no_ne (engine )
854850
855851 def testit (r_idx_type , c_idx_type , index_name ):
856852 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):
878874 yield self .check_basic_frame_series_alignment , engine , parser
879875
880876 def check_basic_series_frame_alignment (self , engine , parser ):
881- skip_if_no_ne (engine )
877+ tm . skip_if_no_ne (engine )
882878
883879 def testit (r_idx_type , c_idx_type , index_name ):
884880 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):
911907 yield self .check_basic_series_frame_alignment , engine , parser
912908
913909 def check_series_frame_commutativity (self , engine , parser ):
914- skip_if_no_ne (engine )
910+ tm . skip_if_no_ne (engine )
915911 args = product (self .lhs_index_types , self .index_types , ('+' , '*' ),
916912 ('index' , 'columns' ))
917913 for r_idx_type , c_idx_type , op , index_name in args :
@@ -934,7 +930,7 @@ def test_series_frame_commutativity(self):
934930 yield self .check_series_frame_commutativity , engine , parser
935931
936932 def check_complex_series_frame_alignment (self , engine , parser ):
937- skip_if_no_ne (engine )
933+ tm . skip_if_no_ne (engine )
938934
939935 import random
940936 args = product (self .lhs_index_types , self .index_types ,
@@ -978,7 +974,7 @@ def test_complex_series_frame_alignment(self):
978974 yield self .check_complex_series_frame_alignment , engine , parser
979975
980976 def check_performance_warning_for_poor_alignment (self , engine , parser ):
981- skip_if_no_ne (engine )
977+ tm . skip_if_no_ne (engine )
982978 df = DataFrame (randn (1000 , 10 ))
983979 s = Series (randn (10000 ))
984980 if engine == 'numexpr' :
@@ -1034,7 +1030,7 @@ class TestOperationsNumExprPandas(tm.TestCase):
10341030 @classmethod
10351031 def setUpClass (cls ):
10361032 super (TestOperationsNumExprPandas , cls ).setUpClass ()
1037- skip_if_no_ne ()
1033+ tm . skip_if_no_ne ()
10381034 cls .engine = 'numexpr'
10391035 cls .parser = 'pandas'
10401036 cls .arith_ops = expr ._arith_ops_syms + expr ._cmp_ops_syms
@@ -1194,7 +1190,7 @@ def test_assignment_fails(self):
11941190 local_dict = {'df' : df , 'df2' : df2 })
11951191
11961192 def test_assignment_column (self ):
1197- skip_if_no_ne ('numexpr' )
1193+ tm . skip_if_no_ne ('numexpr' )
11981194 df = DataFrame (np .random .randn (5 , 2 ), columns = list ('ab' ))
11991195 orig_df = df .copy ()
12001196
@@ -1345,10 +1341,9 @@ class TestOperationsNumExprPython(TestOperationsNumExprPandas):
13451341 @classmethod
13461342 def setUpClass (cls ):
13471343 super (TestOperationsNumExprPython , cls ).setUpClass ()
1348- if not _USE_NUMEXPR :
1349- raise nose .SkipTest ("numexpr engine not installed" )
13501344 cls .engine = 'numexpr'
13511345 cls .parser = 'python'
1346+ tm .skip_if_no_ne (cls .engine )
13521347 cls .arith_ops = expr ._arith_ops_syms + expr ._cmp_ops_syms
13531348 cls .arith_ops = filter (lambda x : x not in ('in' , 'not in' ),
13541349 cls .arith_ops )
@@ -1435,7 +1430,7 @@ def setUpClass(cls):
14351430class TestScope (object ):
14361431
14371432 def check_global_scope (self , e , engine , parser ):
1438- skip_if_no_ne (engine )
1433+ tm . skip_if_no_ne (engine )
14391434 assert_array_equal (_var_s * 2 , pd .eval (e , engine = engine ,
14401435 parser = parser ))
14411436
@@ -1445,7 +1440,7 @@ def test_global_scope(self):
14451440 yield self .check_global_scope , e , engine , parser
14461441
14471442 def check_no_new_locals (self , engine , parser ):
1448- skip_if_no_ne (engine )
1443+ tm . skip_if_no_ne (engine )
14491444 x = 1
14501445 lcls = locals ().copy ()
14511446 pd .eval ('x + 1' , local_dict = lcls , engine = engine , parser = parser )
@@ -1458,7 +1453,7 @@ def test_no_new_locals(self):
14581453 yield self .check_no_new_locals , engine , parser
14591454
14601455 def check_no_new_globals (self , engine , parser ):
1461- skip_if_no_ne (engine )
1456+ tm . skip_if_no_ne (engine )
14621457 x = 1
14631458 gbls = globals ().copy ()
14641459 pd .eval ('x + 1' , engine = engine , parser = parser )
@@ -1471,21 +1466,21 @@ def test_no_new_globals(self):
14711466
14721467
14731468def test_invalid_engine ():
1474- skip_if_no_ne ()
1469+ tm . skip_if_no_ne ()
14751470 assertRaisesRegexp (KeyError , 'Invalid engine \' asdf\' passed' ,
14761471 pd .eval , 'x + y' , local_dict = {'x' : 1 , 'y' : 2 },
14771472 engine = 'asdf' )
14781473
14791474
14801475def test_invalid_parser ():
1481- skip_if_no_ne ()
1476+ tm . skip_if_no_ne ()
14821477 assertRaisesRegexp (KeyError , 'Invalid parser \' asdf\' passed' ,
14831478 pd .eval , 'x + y' , local_dict = {'x' : 1 , 'y' : 2 },
14841479 parser = 'asdf' )
14851480
14861481
14871482def check_is_expr_syntax (engine ):
1488- skip_if_no_ne (engine )
1483+ tm . skip_if_no_ne (engine )
14891484 s = 1
14901485 valid1 = 's + 1'
14911486 valid2 = '__y + _xx'
@@ -1494,7 +1489,7 @@ def check_is_expr_syntax(engine):
14941489
14951490
14961491def check_is_expr_names (engine ):
1497- skip_if_no_ne (engine )
1492+ tm . skip_if_no_ne (engine )
14981493 r , s = 1 , 2
14991494 valid = 's + r'
15001495 invalid = '__y + __x'
@@ -1517,7 +1512,7 @@ def test_is_expr_names():
15171512
15181513
15191514def check_disallowed_nodes (engine , parser ):
1520- skip_if_no_ne (engine )
1515+ tm . skip_if_no_ne (engine )
15211516 VisitorClass = _parsers [parser ]
15221517 uns_ops = VisitorClass .unsupported_nodes
15231518 inst = VisitorClass ('x + 1' , engine , parser )
@@ -1532,7 +1527,7 @@ def test_disallowed_nodes():
15321527
15331528
15341529def check_syntax_error_exprs (engine , parser ):
1535- skip_if_no_ne (engine )
1530+ tm . skip_if_no_ne (engine )
15361531 e = 's +'
15371532 assert_raises (SyntaxError , pd .eval , e , engine = engine , parser = parser )
15381533
@@ -1543,7 +1538,7 @@ def test_syntax_error_exprs():
15431538
15441539
15451540def check_name_error_exprs (engine , parser ):
1546- skip_if_no_ne (engine )
1541+ tm . skip_if_no_ne (engine )
15471542 e = 's + t'
15481543 assert_raises (NameError , pd .eval , e , engine = engine , parser = parser )
15491544
@@ -1553,6 +1548,33 @@ def test_name_error_exprs():
15531548 yield check_name_error_exprs , engine , parser
15541549
15551550
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+
15561578if __name__ == '__main__' :
15571579 nose .runmodule (argv = [__file__ , '-vvs' , '-x' , '--pdb' , '--pdb-failure' ],
15581580 exit = False )
0 commit comments