@@ -781,6 +781,86 @@ class mydialect(csv.Dialect):
781781 with self .assertRaisesRegex (TypeError , "'delimeter'" ):
782782 copy .replace (dialect , delimeter = ':' )
783783
784+ def test_replace_dialect_slots (self ):
785+ # Dialect with single string __slots__
786+ class SingleSlotDialect (csv .excel ):
787+ __slots__ = 'delimiter'
788+ def __init__ (self ):
789+ self .delimiter = ';'
790+ super ().__init__ ()
791+
792+ d1 = SingleSlotDialect ()
793+ r1 = copy .replace (d1 , quoting = csv .QUOTE_ALL )
794+ self .assertIsInstance (r1 , SingleSlotDialect )
795+ self .assertEqual (r1 .delimiter , ';' )
796+ self .assertEqual (r1 .quoting , csv .QUOTE_ALL )
797+ self .assertEqual (list (csv .reader (['a;b' ], r1 )), [['a' , 'b' ]])
798+
799+ # Replacing a slot-backed attribute
800+ r1_mod = copy .replace (d1 , delimiter = ':' )
801+ self .assertEqual (r1_mod .delimiter , ':' )
802+ self .assertEqual (list (csv .reader (['a:b' ], r1_mod )), [['a' , 'b' ]])
803+
804+ # Dialect with tuple __slots__
805+ class MultiSlotDialect (csv .excel ):
806+ __slots__ = ('delimiter' , 'quotechar' )
807+ def __init__ (self ):
808+ self .delimiter = '|'
809+ self .quotechar = '#'
810+ super ().__init__ ()
811+
812+ d2 = MultiSlotDialect ()
813+ r2 = copy .replace (d2 , quoting = csv .QUOTE_MINIMAL )
814+ self .assertIsInstance (r2 , MultiSlotDialect )
815+ self .assertEqual (r2 .delimiter , '|' )
816+ self .assertEqual (r2 .quotechar , '#' )
817+ self .assertEqual (list (csv .reader (['#a|b#|c' ], r2 )), [['a|b' , 'c' ]])
818+
819+ # Subclassing with __slots__ across multiple inheritance levels
820+ class BaseSlotDialect (csv .excel ):
821+ __slots__ = ('delimiter' ,)
822+ def __init__ (self ):
823+ self .delimiter = ';'
824+ super ().__init__ ()
825+
826+ class SubSlotDialect (BaseSlotDialect ):
827+ __slots__ = ('quotechar' , 'escapechar' )
828+ def __init__ (self ):
829+ super ().__init__ ()
830+ self .quotechar = '~'
831+ self .escapechar = '\\ '
832+
833+ d3 = SubSlotDialect ()
834+ r3 = copy .replace (d3 , doublequote = False )
835+ self .assertIsInstance (r3 , SubSlotDialect )
836+ self .assertEqual (r3 .delimiter , ';' )
837+ self .assertEqual (r3 .quotechar , '~' )
838+ self .assertEqual (r3 .escapechar , '\\ ' )
839+ self .assertFalse (r3 .doublequote )
840+ self .assertEqual (list (csv .reader (['~a;b~;c' ], r3 )), [['a;b' , 'c' ]])
841+
842+ # Unset slot attribute on instance
843+ class UnsetSlotDialect (csv .excel ):
844+ __slots__ = ('delimiter' ,)
845+
846+ d4 = UnsetSlotDialect ()
847+ r4 = copy .replace (d4 , quoting = csv .QUOTE_ALL )
848+ self .assertIsInstance (r4 , UnsetSlotDialect )
849+ self .assertFalse (hasattr (r4 , 'delimiter' ))
850+
851+ # Mapping __slots__
852+ class DictSlotDialect (csv .excel ):
853+ __slots__ = {'delimiter' : 'doc for delimiter' }
854+ def __init__ (self ):
855+ self .delimiter = '\t '
856+ super ().__init__ ()
857+
858+ d5 = DictSlotDialect ()
859+ r5 = copy .replace (d5 , quoting = csv .QUOTE_ALL )
860+ self .assertIsInstance (r5 , DictSlotDialect )
861+ self .assertEqual (r5 .delimiter , '\t ' )
862+ self .assertEqual (list (csv .reader (['a\t b' ], r5 )), [['a' , 'b' ]])
863+
784864 def test_pickle (self ):
785865 for name in csv .list_dialects ():
786866 dialect = csv .get_dialect (name )
0 commit comments