1616
1717import struct
1818import sys
19+ import typing
1920from io import BytesIO
2021from math import (
2122 isnan ,
2425from uuid import uuid4
2526
2627import numpy as np
27- import pandas as pd
2828import pytest
2929
3030from neo4j ._codec .packstream import Structure
3636)
3737
3838
39+ HAS_PD = True
40+ if typing .TYPE_CHECKING :
41+ import pandas as pd
42+ else :
43+ try :
44+ import pandas as pd
45+ except ImportError :
46+ pd = None
47+ HAS_PD = False
48+
3949standard_ascii = [chr (i ) for i in range (128 )]
4050not_ascii = "♥O◘♦♥O◘♦"
4151SKIP_PANDAS_INT_AS_INT64 = (
42- pd .Series ([], dtype = int ).dtype .itemsize < 8
52+ HAS_PD
53+ and pd .Series ([], dtype = int ).dtype .itemsize < 8
4354 and sys .version_info < (3 , 9 )
4455 and sys .platform == "win32"
4556)
@@ -179,10 +190,23 @@ def str_type(request):
179190
180191
181192@pytest .fixture (
182- params = (list , tuple , np .array , pd .Series , pd .array , pd .arrays .SparseArray )
193+ params = (
194+ list ,
195+ tuple ,
196+ np .array ,
197+ * (
198+ (
199+ pd .Series ,
200+ pd .array ,
201+ pd .arrays .SparseArray ,
202+ )
203+ if HAS_PD
204+ else ()
205+ ),
206+ )
183207)
184208def sequence_type (request ):
185- if request .param is pd .Series :
209+ if HAS_PD and request .param is pd .Series :
186210
187211 def constructor (value ):
188212 if not value :
@@ -194,15 +218,22 @@ def constructor(value):
194218
195219
196220class TestPackStream :
197- @pytest .mark .parametrize ("value" , (None , pd .NA ))
221+ @pytest .mark .parametrize (
222+ "value" ,
223+ (None , * ((pd .NA ,) if HAS_PD else ())),
224+ )
198225 def test_none (self , value , assert_packable ):
199226 assert_packable (value , b"\xc0 " , None )
200227
201228 def test_boolean (self , bool_type , assert_packable ):
202229 assert_packable (bool_type (True ), b"\xc3 " )
203230 assert_packable (bool_type (False ), b"\xc2 " )
204231
205- @pytest .mark .parametrize ("dtype" , (bool , pd .BooleanDtype ()))
232+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
233+ @pytest .mark .parametrize (
234+ "dtype" ,
235+ (bool , * ((pd .BooleanDtype (),) if HAS_PD else ())),
236+ )
206237 def test_boolean_pandas_series (self , dtype , assert_packable ):
207238 value = [True , False ]
208239 value_series = pd .Series (value , dtype = dtype )
@@ -215,19 +246,26 @@ def test_negative_tiny_int(self, int_type, assert_packable):
215246 continue # not representable
216247 assert_packable (z_typed , bytes (bytearray ([z + 0x100 ])))
217248
249+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
218250 @pytest .mark .parametrize (
219251 "dtype" ,
220252 (
221253 int ,
222- pd .Int8Dtype (),
223- pd .Int16Dtype (),
224- pd .Int32Dtype (),
225- pd .Int64Dtype (),
226254 np .int8 ,
227255 np .int16 ,
228256 np .int32 ,
229257 np .int64 ,
230258 np .longlong ,
259+ * (
260+ (
261+ pd .Int8Dtype (),
262+ pd .Int16Dtype (),
263+ pd .Int32Dtype (),
264+ pd .Int64Dtype (),
265+ )
266+ if HAS_PD
267+ else ()
268+ ),
231269 ),
232270 )
233271 def test_negative_tiny_int_pandas_series (self , dtype , assert_packable ):
@@ -292,6 +330,7 @@ def test_positive_int64(self, int_type, assert_packable):
292330 expected = b"\xcb " + struct .pack (">q" , z )
293331 assert_packable (z_typed , expected )
294332
333+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
295334 @pytest .mark .parametrize (
296335 "dtype" ,
297336 (
@@ -302,12 +341,18 @@ def test_positive_int64(self, int_type, assert_packable):
302341 reason = "Legacy pandas treating int as int32" ,
303342 ),
304343 ),
305- pd .Int64Dtype (),
306- pd .UInt64Dtype (),
307344 np .int64 ,
308345 np .longlong ,
309346 np .uint64 ,
310347 np .ulonglong ,
348+ * (
349+ (
350+ pd .Int64Dtype (),
351+ pd .UInt64Dtype (),
352+ )
353+ if HAS_PD
354+ else ()
355+ ),
311356 ),
312357 )
313358 def test_positive_int64_pandas_series (self , dtype , assert_packable ):
@@ -326,6 +371,7 @@ def test_negative_int64(self, int_type, assert_packable):
326371 expected = b"\xcb " + struct .pack (">q" , z )
327372 assert_packable (z_typed , expected )
328373
374+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
329375 @pytest .mark .parametrize (
330376 "dtype" ,
331377 (
@@ -336,9 +382,9 @@ def test_negative_int64(self, int_type, assert_packable):
336382 reason = "Legacy pandas treating int as int32" ,
337383 ),
338384 ),
339- pd .Int64Dtype (),
340385 np .int64 ,
341386 np .longlong ,
387+ * ((pd .Int64Dtype (),) if HAS_PD else ()),
342388 ),
343389 )
344390 def test_negative_int64_pandas_series (self , dtype , assert_packable ):
@@ -383,16 +429,23 @@ def test_float(self, float_type, assert_packable):
383429 expected = b"\xc1 " + struct .pack (">d" , float (z_typed ))
384430 assert_packable (z_typed , expected )
385431
432+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
386433 @pytest .mark .parametrize (
387434 "dtype" ,
388435 (
389436 float ,
390- pd .Float32Dtype (),
391- pd .Float64Dtype (),
392437 np .float16 ,
393438 np .float32 ,
394439 np .float64 ,
395440 np .longdouble ,
441+ * (
442+ (
443+ pd .Float32Dtype (),
444+ pd .Float64Dtype (),
445+ )
446+ if HAS_PD
447+ else ()
448+ ),
396449 ),
397450 )
398451 def test_float_pandas_series (
@@ -441,6 +494,7 @@ def test_bytes_32(self, bytes_type, assert_packable):
441494 b_typed = bytes_type (b )
442495 assert_packable (b_typed , b"\xce \x00 \x01 \x38 \x80 " + b )
443496
497+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
444498 def test_bytes_pandas_series (self , assert_packable ):
445499 for b , header in (
446500 (b"" , b"\xcc \x00 " ),
@@ -489,13 +543,20 @@ def test_unicode_string(self, str_type, assert_packable):
489543 t_typed = str_type (t )
490544 assert_packable (t_typed , bytes (bytearray ([0x80 + len (b )])) + b )
491545
546+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
492547 @pytest .mark .parametrize (
493548 "dtype" ,
494549 (
495550 str ,
496551 np .str_ ,
497- pd .StringDtype ("python" ),
498- pd .StringDtype ("pyarrow" ),
552+ * (
553+ (
554+ pd .StringDtype ("python" ),
555+ pd .StringDtype ("pyarrow" ),
556+ )
557+ if HAS_PD
558+ else ()
559+ ),
499560 ),
500561 )
501562 def test_string_pandas_series (self , dtype , assert_packable ):
@@ -555,6 +616,7 @@ def test_nested_lists(self, sequence_type, assert_packable):
555616 l_typed = sequence_type ([sequence_type ([sequence_type ([])])])
556617 assert_packable (l_typed , b"\x91 \x91 \x90 " , list_ )
557618
619+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
558620 @pytest .mark .parametrize ("as_series" , (True , False ))
559621 def test_list_pandas_categorical (self , as_series , pack , assert_packable ):
560622 animals = ["cat" , "dog" , "cat" , "cat" , "dog" , "horse" ]
@@ -637,10 +699,12 @@ def test_map_key_string_32(self, assert_packable):
637699 data_out = b"\xa1 \xd2 \x00 \x01 \x38 \x80 " + key .encode ("utf-8" ) + b"\x01 "
638700 assert_packable (d , data_out )
639701
702+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
640703 def test_empty_dataframe_maps (self , assert_packable ):
641704 df = pd .DataFrame ()
642705 assert_packable (df , b"\xa0 " , {})
643706
707+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
644708 @pytest .mark .parametrize ("size" , range (0x10 ))
645709 def test_tiny_dataframes_maps (self , assert_packable , size ):
646710 data_in = {}
@@ -661,10 +725,16 @@ def test_map_size_overflow(self):
661725 ("map_" , "exc_type" ),
662726 (
663727 ({1 : "1" }, TypeError ),
664- (pd .DataFrame ({1 : ["1" ]}), TypeError ),
665- (pd .DataFrame ({(1 , 2 ): ["1" ]}), TypeError ),
666728 ({"x" : {1 : "eins" , 2 : "zwei" , 3 : "drei" }}, TypeError ),
667729 ({"x" : {(1 , 2 ): "1+2i" , (2 , 0 ): "2" }}, TypeError ),
730+ * (
731+ (
732+ (pd .DataFrame ({1 : ["1" ]}), TypeError ),
733+ (pd .DataFrame ({(1 , 2 ): ["1" ]}), TypeError ),
734+ )
735+ if HAS_PD
736+ else ()
737+ ),
668738 ),
669739 )
670740 def test_map_key_type (self , packer_with_buffer , map_ , exc_type ):
0 commit comments