1515
1616
1717import struct
18+ import typing
1819from contextlib import suppress
1920from io import BytesIO
2021from math import (
2425from uuid import uuid4
2526
2627import numpy as np
27- import pandas as pd
2828import pyarrow as pa
2929import pytest
3030
3737)
3838
3939
40+ HAS_PD = True
41+ if typing .TYPE_CHECKING :
42+ import pandas as pd
43+ else :
44+ try :
45+ import pandas as pd
46+ except ImportError :
47+ pd = None
48+ HAS_PD = False
49+
4050standard_ascii = [chr (i ) for i in range (128 )]
4151not_ascii = "♥O◘♦♥O◘♦"
4252
@@ -180,47 +190,58 @@ def str_type(request):
180190
181191@pytest .fixture (
182192 params = (
183- list ,
184- tuple ,
185- np .array ,
186- pd .Series ,
187- pd .array ,
188- pd .arrays .SparseArray ,
189- pd .arrays .NumpyExtensionArray ,
190- pd .arrays .ArrowExtensionArray ,
191- ),
192- ids = (
193- "list" ,
194- "tuple" ,
195- "np.array" ,
196- "pd.Series" ,
197- "pd.array" ,
198- "pd.arrays.SparseArray" ,
199- "pd.arrays.NumpyExtensionArray" ,
200- "pd.arrays.ArrowExtensionArray" ,
201- ),
193+ pytest .param (list , id = "list" ),
194+ pytest .param (tuple , id = "tuple" ),
195+ pytest .param (np .array , id = "np.array" ),
196+ * (
197+ (
198+ pytest .param (
199+ pd .Series ,
200+ id = "pd.Series" ,
201+ ),
202+ pytest .param (
203+ pd .array ,
204+ id = "pd.array" ,
205+ ),
206+ pytest .param (
207+ pd .arrays .SparseArray ,
208+ id = "pd.arrays.SparseArray" ,
209+ ),
210+ pytest .param (
211+ pd .arrays .NumpyExtensionArray ,
212+ id = "pd.arrays.NumpyExtensionArray" ,
213+ ),
214+ pytest .param (
215+ pd .arrays .ArrowExtensionArray ,
216+ id = "pd.arrays.ArrowExtensionArray" ,
217+ ),
218+ )
219+ if HAS_PD
220+ else ()
221+ ),
222+ )
202223)
203224def sequence_type (request ):
204- if request .param is pd .Series :
225+ if HAS_PD and request .param is pd .Series :
205226
206227 def constructor (value ):
207228 if not value :
208229 return pd .Series (dtype = object )
209230 return pd .Series (value )
210231
211- elif request .param is pd .array and pd .__version__ >= "3" :
232+ elif HAS_PD and request .param is pd .array and pd .__version__ >= "3" :
212233
213234 def constructor (value ):
214235 with suppress (ValueError ):
215236 return pd .array (value )
216237 return pd .array (value , dtype = object )
217238
218- elif request .param is pd .arrays .NumpyExtensionArray :
239+ elif HAS_PD and request .param is pd .arrays .NumpyExtensionArray :
219240
220241 def constructor (value ):
221242 return pd .arrays .NumpyExtensionArray (np .array (value ))
222243
223- elif request .param is pd .arrays .ArrowExtensionArray :
244+ elif HAS_PD and request .param is pd .arrays .ArrowExtensionArray :
224245
225246 def constructor (value ):
226247 def _map_value (v ):
@@ -240,15 +261,22 @@ def _map_value(v):
240261
241262
242263class TestPackStream :
243- @pytest .mark .parametrize ("value" , (None , pd .NA ))
264+ @pytest .mark .parametrize (
265+ "value" ,
266+ (None , * ((pd .NA ,) if HAS_PD else ())),
267+ )
244268 def test_none (self , value , assert_packable ):
245269 assert_packable (value , b"\xc0 " , None )
246270
247271 def test_boolean (self , bool_type , assert_packable ):
248272 assert_packable (bool_type (True ), b"\xc3 " )
249273 assert_packable (bool_type (False ), b"\xc2 " )
250274
251- @pytest .mark .parametrize ("dtype" , (bool , pd .BooleanDtype ()))
275+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
276+ @pytest .mark .parametrize (
277+ "dtype" ,
278+ (bool , * ((pd .BooleanDtype (),) if HAS_PD else ())),
279+ )
252280 def test_boolean_pandas_series (self , dtype , assert_packable ):
253281 value = [True , False ]
254282 value_series = pd .Series (value , dtype = dtype )
@@ -261,19 +289,26 @@ def test_negative_tiny_int(self, int_type, assert_packable):
261289 continue # not representable
262290 assert_packable (z_typed , bytes (bytearray ([z + 0x100 ])))
263291
292+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
264293 @pytest .mark .parametrize (
265294 "dtype" ,
266295 (
267296 int ,
268- pd .Int8Dtype (),
269- pd .Int16Dtype (),
270- pd .Int32Dtype (),
271- pd .Int64Dtype (),
272297 np .int8 ,
273298 np .int16 ,
274299 np .int32 ,
275300 np .int64 ,
276301 np .longlong ,
302+ * (
303+ (
304+ pd .Int8Dtype (),
305+ pd .Int16Dtype (),
306+ pd .Int32Dtype (),
307+ pd .Int64Dtype (),
308+ )
309+ if HAS_PD
310+ else ()
311+ ),
277312 ),
278313 )
279314 def test_negative_tiny_int_pandas_series (self , dtype , assert_packable ):
@@ -338,16 +373,23 @@ def test_positive_int64(self, int_type, assert_packable):
338373 expected = b"\xcb " + struct .pack (">q" , z )
339374 assert_packable (z_typed , expected )
340375
376+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
341377 @pytest .mark .parametrize (
342378 "dtype" ,
343379 (
344380 int ,
345- pd .Int64Dtype (),
346- pd .UInt64Dtype (),
347381 np .int64 ,
348382 np .longlong ,
349383 np .uint64 ,
350384 np .ulonglong ,
385+ * (
386+ (
387+ pd .Int64Dtype (),
388+ pd .UInt64Dtype (),
389+ )
390+ if HAS_PD
391+ else ()
392+ ),
351393 ),
352394 )
353395 def test_positive_int64_pandas_series (self , dtype , assert_packable ):
@@ -366,13 +408,14 @@ def test_negative_int64(self, int_type, assert_packable):
366408 expected = b"\xcb " + struct .pack (">q" , z )
367409 assert_packable (z_typed , expected )
368410
411+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
369412 @pytest .mark .parametrize (
370413 "dtype" ,
371414 (
372415 int ,
373- pd .Int64Dtype (),
374416 np .int64 ,
375417 np .longlong ,
418+ * ((pd .Int64Dtype (),) if HAS_PD else ()),
376419 ),
377420 )
378421 def test_negative_int64_pandas_series (self , dtype , assert_packable ):
@@ -417,16 +460,23 @@ def test_float(self, float_type, assert_packable):
417460 expected = b"\xc1 " + struct .pack (">d" , float (z_typed ))
418461 assert_packable (z_typed , expected )
419462
463+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
420464 @pytest .mark .parametrize (
421465 "dtype" ,
422466 (
423467 float ,
424- pd .Float32Dtype (),
425- pd .Float64Dtype (),
426468 np .float16 ,
427469 np .float32 ,
428470 np .float64 ,
429471 np .longdouble ,
472+ * (
473+ (
474+ pd .Float32Dtype (),
475+ pd .Float64Dtype (),
476+ )
477+ if HAS_PD
478+ else ()
479+ ),
430480 ),
431481 )
432482 def test_float_pandas_series (
@@ -475,6 +525,7 @@ def test_bytes_32(self, bytes_type, assert_packable):
475525 b_typed = bytes_type (b )
476526 assert_packable (b_typed , b"\xce \x00 \x01 \x38 \x80 " + b )
477527
528+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
478529 def test_bytes_pandas_series (self , assert_packable ):
479530 for b , header in (
480531 (b"" , b"\xcc \x00 " ),
@@ -523,13 +574,20 @@ def test_unicode_string(self, str_type, assert_packable):
523574 t_typed = str_type (t )
524575 assert_packable (t_typed , bytes (bytearray ([0x80 + len (b )])) + b )
525576
577+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
526578 @pytest .mark .parametrize (
527579 "dtype" ,
528580 (
529581 str ,
530582 np .str_ ,
531- pd .StringDtype ("python" ),
532- pd .StringDtype ("pyarrow" ),
583+ * (
584+ (
585+ pd .StringDtype ("python" ),
586+ pd .StringDtype ("pyarrow" ),
587+ )
588+ if HAS_PD
589+ else ()
590+ ),
533591 ),
534592 )
535593 def test_string_pandas_series (self , dtype , assert_packable ):
@@ -593,6 +651,7 @@ def test_nested_lists(self, sequence_type, inner_as_list, assert_packable):
593651 l_typed = sequence_type ([sequence_type ([sequence_type ([])])])
594652 assert_packable (l_typed , b"\x91 \x91 \x90 " , list_ )
595653
654+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
596655 @pytest .mark .parametrize ("as_series" , (True , False ))
597656 def test_list_pandas_categorical (self , as_series , pack , assert_packable ):
598657 animals = ["cat" , "dog" , "cat" , "cat" , "dog" , "horse" ]
@@ -675,10 +734,12 @@ def test_map_key_string_32(self, assert_packable):
675734 data_out = b"\xa1 \xd2 \x00 \x01 \x38 \x80 " + key .encode ("utf-8" ) + b"\x01 "
676735 assert_packable (d , data_out )
677736
737+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
678738 def test_empty_dataframe_maps (self , assert_packable ):
679739 df = pd .DataFrame ()
680740 assert_packable (df , b"\xa0 " , {})
681741
742+ @pytest .mark .skipif (pd is None , reason = "pandas not installed" )
682743 @pytest .mark .parametrize ("size" , range (0x10 ))
683744 def test_tiny_dataframes_maps (self , assert_packable , size ):
684745 data_in = {}
@@ -699,10 +760,16 @@ def test_map_size_overflow(self):
699760 ("map_" , "exc_type" ),
700761 (
701762 ({1 : "1" }, TypeError ),
702- (pd .DataFrame ({1 : ["1" ]}), TypeError ),
703- (pd .DataFrame ({(1 , 2 ): ["1" ]}), TypeError ),
704763 ({"x" : {1 : "eins" , 2 : "zwei" , 3 : "drei" }}, TypeError ),
705764 ({"x" : {(1 , 2 ): "1+2i" , (2 , 0 ): "2" }}, TypeError ),
765+ * (
766+ (
767+ (pd .DataFrame ({1 : ["1" ]}), TypeError ),
768+ (pd .DataFrame ({(1 , 2 ): ["1" ]}), TypeError ),
769+ )
770+ if HAS_PD
771+ else ()
772+ ),
706773 ),
707774 )
708775 def test_map_key_type (self , packer_with_buffer , map_ , exc_type ):
0 commit comments