| 1772 | |
| 1773 | |
| 1774 | def _replace(self, /, **changes): |
| 1775 | # We're going to mutate 'changes', but that's okay because it's a |
| 1776 | # new dict, even if called with 'replace(self, **my_changes)'. |
| 1777 | |
| 1778 | # It's an error to have init=False fields in 'changes'. |
| 1779 | # If a field is not in 'changes', read its value from the provided 'self'. |
| 1780 | |
| 1781 | for f in getattr(self, _FIELDS).values(): |
| 1782 | # Only consider normal fields or InitVars. |
| 1783 | if f._field_type is _FIELD_CLASSVAR: |
| 1784 | continue |
| 1785 | |
| 1786 | if not f.init: |
| 1787 | # Error if this field is specified in changes. |
| 1788 | if f.name in changes: |
| 1789 | raise TypeError(f'field {f.name} is declared with ' |
| 1790 | f'init=False, it cannot be specified with ' |
| 1791 | f'replace()') |
| 1792 | continue |
| 1793 | |
| 1794 | if f.name not in changes: |
| 1795 | if f._field_type is _FIELD_INITVAR and f.default is MISSING: |
| 1796 | raise TypeError(f"InitVar {f.name!r} " |
| 1797 | f'must be specified with replace()') |
| 1798 | changes[f.name] = getattr(self, f.name) |
| 1799 | |
| 1800 | # Create the new object, which calls __init__() and |
| 1801 | # __post_init__() (if defined), using all of the init fields we've |
| 1802 | # added and/or left in 'changes'. If there are values supplied in |
| 1803 | # changes that aren't fields, this will correctly raise a |
| 1804 | # TypeError. |
| 1805 | return self.__class__(**changes) |