Skip to content

gh-160106: Fix csv.Dialect.__replace__() losing slot-backed attributes - #160641

Open
saiyam-jain-dev wants to merge 3 commits into
python:mainfrom
saiyam-jain-dev:gh-160106-csv-dialect-replace-slots
Open

saiyam-jain-dev wants to merge 3 commits into
python:mainfrom
saiyam-jain-dev:gh-160106-csv-dialect-replace-slots

Conversation

@saiyam-jain-dev

@saiyam-jain-dev saiyam-jain-dev commented Oct 25, 2026 •

Copy link
Copy Markdown

Summary

Fixes #160106

copy.replace() on a csv.Dialect subclass that stores dialect options in slots silently drops those slot-backed attributes. Dialect.replace() only copied self.dict, so any attribute stored in a slot (rather than dict) was missing from the new object, causing csv.reader/csv.writer to fall back to the default comma delimiter.

Reproducer (before fix)

`python
import copy, csv

class SemicolonDialect(csv.excel):
slots = ('delimiter',)
def init(self):
self.delimiter = ';'
super().init()

dialect = SemicolonDialect()
replacement = copy.replace(dialect, quoting=csv.QUOTE_ALL)

print(list(csv.reader(['alpha;beta'], dialect))) # [['alpha', 'beta']]
print(list(csv.reader(['alpha;beta'], replacement))) # [['alpha;beta']] ← BUG
`

Fix

In Dialect.replace(), after copying self.dict, iterate the MRO for slots entries and copy any dialect attribute not already set via dict before applying the caller's changes.

Testing

The existing csv test suite continues to pass. The reproducer from the issue now produces [['alpha', 'beta']] for both readers.

@python-cla-bot

python-cla-bot Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@bedevere-app

bedevere-app Bot commented Oct 25, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

…ributes

copy.replace() on a csv.Dialect subclass that stores dialect options in
__slots__ would silently drop those attributes.  __replace__() only copied
self.__dict__, so any attribute stored in a slot was missing from the new
object, causing csv.reader/writer to fall back to the default value.

Fix by iterating the MRO for __slots__ entries and copying any dialect
attribute that was not already set via __dict__.
@saiyam-jain-dev
saiyam-jain-dev force-pushed the gh-160106-csv-dialect-replace-slots branch from 68b5d3d to 181df51 Compare October 25, 2026 15:28
@bedevere-app

bedevere-app Bot commented Oct 25, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@sobolevn sobolevn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, test this properly. provide multiple case with different __slots__ setups.

Comment thread Lib/csv.py
new.__dict__.update(self.__dict__)
# Copy slot-backed dialect attributes from the instance's MRO,
# since __dict__ only captures attributes stored in __dict__.
for cls in type(self).__mro__:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing: it looks like a rather popular task, including user-facing objects. Maybe we should provide a generic helper for this? You can open a new issue to discuss this

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, having a standard helper for copying/replacing slotted attributes would be really useful across the stdlib and user code. I'll open an issue for it so we can discuss the design.

@saiyam-jain-dev

Copy link
Copy Markdown
Author

Added tests in est_csv covering multiple slot setups:

  • Single string slots
  • Tuple slots with multiple dialect attributes
  • Inheritance chains with slots defined across classes
  • Unset slot attributes on the instance
  • Mapping/dict slots
  • Replacing a slotted attribute via copy.replace()

Also updated the implementation to use setattr(): slot descriptors shadow dict, so writing directly to dict left the slots uninitialized on the new instance and readers still fell back to defaults. It now sets the descriptors directly and handles slotted overrides cleanly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

copy.replace loses slot-backed CSV dialect options

2 participants