Skip to content

Commit de5564a

Browse files
committed
Remove obsolete checks for copy.replace support
1 parent d834320 commit de5564a

File tree

3 files changed

+3
-361
lines changed

3 files changed

+3
-361
lines changed

Lib/test/test_ast/test_ast.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,7 +1412,7 @@ def test_replace_reject_missing_field(self):
14121412

14131413
self.assertRaises(AttributeError, getattr, node, 'id')
14141414
self.assertIs(node.ctx, context)
1415-
msg = "Name.__replace__ missing 1 keyword argument: 'id'."
1415+
msg = "ast.Name.__init__ missing 1 required positional argument: 'id'"
14161416
with self.assertRaisesRegex(TypeError, re.escape(msg)):
14171417
copy.replace(node)
14181418
# assert that there is no side-effect
@@ -1449,7 +1449,7 @@ def test_replace_reject_known_custom_instance_fields_commits(self):
14491449

14501450
# explicit rejection of known instance fields
14511451
self.assertHasAttr(node, 'extra')
1452-
msg = "Name.__replace__ got an unexpected keyword argument 'extra'."
1452+
msg = "ast.Name.__init__ got an unexpected keyword argument 'extra'"
14531453
with self.assertRaisesRegex(TypeError, re.escape(msg)):
14541454
copy.replace(node, extra=1)
14551455
# assert that there is no side-effect
@@ -1463,7 +1463,7 @@ def test_replace_reject_unknown_instance_fields(self):
14631463

14641464
# explicit rejection of unknown extra fields
14651465
self.assertRaises(AttributeError, getattr, node, 'unknown')
1466-
msg = "Name.__replace__ got an unexpected keyword argument 'unknown'."
1466+
msg = "ast.Name.__init__ got an unexpected keyword argument 'unknown'"
14671467
with self.assertRaisesRegex(TypeError, re.escape(msg)):
14681468
copy.replace(node, unknown=1)
14691469
# assert that there is no side-effect

Parser/asdl_c.py

Lines changed: 0 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,182 +1212,6 @@ def visitModule(self, mod):
12121212
return result;
12131213
}
12141214
1215-
/*
1216-
* Perform the following validations:
1217-
*
1218-
* - All keyword arguments are known 'fields' or 'attributes'.
1219-
* - No field or attribute would be left unfilled after copy.replace().
1220-
*
1221-
* On success, this returns 1. Otherwise, set a TypeError
1222-
* exception and returns -1 (no exception is set if some
1223-
* other internal errors occur).
1224-
*
1225-
* Parameters
1226-
*
1227-
* self The AST node instance.
1228-
* dict The AST node instance dictionary (self.__dict__).
1229-
* fields The list of fields (self._fields).
1230-
* attributes The list of attributes (self._attributes).
1231-
* kwargs Keyword arguments passed to ast_type_replace().
1232-
*
1233-
* The 'dict', 'fields', 'attributes' and 'kwargs' arguments can be NULL.
1234-
*
1235-
* Note: this function can be removed in 3.15 since the verification
1236-
* will be done inside the constructor.
1237-
*/
1238-
static inline int
1239-
ast_type_replace_check(PyObject *self,
1240-
PyObject *dict,
1241-
PyObject *fields,
1242-
PyObject *attributes,
1243-
PyObject *kwargs)
1244-
{
1245-
// While it is possible to make some fast paths that would avoid
1246-
// allocating objects on the stack, this would cost us readability.
1247-
// For instance, if 'fields' and 'attributes' are both empty, and
1248-
// 'kwargs' is not empty, we could raise a TypeError immediately.
1249-
PyObject *expecting = PySet_New(fields);
1250-
if (expecting == NULL) {
1251-
return -1;
1252-
}
1253-
if (attributes) {
1254-
if (_PySet_Update(expecting, attributes) < 0) {
1255-
Py_DECREF(expecting);
1256-
return -1;
1257-
}
1258-
}
1259-
// Any keyword argument that is neither a field nor attribute is rejected.
1260-
// We first need to check whether a keyword argument is accepted or not.
1261-
// If all keyword arguments are accepted, we compute the required fields
1262-
// and attributes. A field or attribute is not needed if:
1263-
//
1264-
// 1) it is given in 'kwargs', or
1265-
// 2) it already exists on 'self'.
1266-
if (kwargs) {
1267-
Py_ssize_t pos = 0;
1268-
PyObject *key, *value;
1269-
while (PyDict_Next(kwargs, &pos, &key, &value)) {
1270-
int rc = PySet_Discard(expecting, key);
1271-
if (rc < 0) {
1272-
Py_DECREF(expecting);
1273-
return -1;
1274-
}
1275-
if (rc == 0) {
1276-
PyErr_Format(PyExc_TypeError,
1277-
"%.400s.__replace__ got an unexpected keyword "
1278-
"argument '%U'.", Py_TYPE(self)->tp_name, key);
1279-
Py_DECREF(expecting);
1280-
return -1;
1281-
}
1282-
}
1283-
}
1284-
// check that the remaining fields or attributes would be filled
1285-
if (dict) {
1286-
Py_ssize_t pos = 0;
1287-
PyObject *key, *value;
1288-
while (PyDict_Next(dict, &pos, &key, &value)) {
1289-
// Mark fields or attributes that are found on the instance
1290-
// as non-mandatory. If they are not given in 'kwargs', they
1291-
// will be shallow-coied; otherwise, they would be replaced
1292-
// (not in this function).
1293-
if (PySet_Discard(expecting, key) < 0) {
1294-
Py_DECREF(expecting);
1295-
return -1;
1296-
}
1297-
}
1298-
if (attributes) {
1299-
// Some attributes may or may not be present at runtime.
1300-
// In particular, now that we checked whether 'kwargs'
1301-
// is correct or not, we allow any attribute to be missing.
1302-
//
1303-
// Note that fields must still be entirely determined when
1304-
// calling the constructor later.
1305-
PyObject *unused = PyObject_CallMethodOneArg(expecting,
1306-
&_Py_ID(difference_update),
1307-
attributes);
1308-
if (unused == NULL) {
1309-
Py_DECREF(expecting);
1310-
return -1;
1311-
}
1312-
Py_DECREF(unused);
1313-
}
1314-
}
1315-
1316-
// Discard fields from 'expecting' that default to None
1317-
PyObject *field_types = NULL;
1318-
if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self),
1319-
&_Py_ID(_field_types),
1320-
&field_types) < 0)
1321-
{
1322-
Py_DECREF(expecting);
1323-
return -1;
1324-
}
1325-
if (field_types != NULL) {
1326-
Py_ssize_t pos = 0;
1327-
PyObject *field_name, *field_type;
1328-
while (PyDict_Next(field_types, &pos, &field_name, &field_type)) {
1329-
if (_PyUnion_Check(field_type)) {
1330-
// optional field
1331-
if (PySet_Discard(expecting, field_name) < 0) {
1332-
Py_DECREF(expecting);
1333-
Py_DECREF(field_types);
1334-
return -1;
1335-
}
1336-
}
1337-
}
1338-
Py_DECREF(field_types);
1339-
}
1340-
1341-
// Now 'expecting' contains the fields or attributes
1342-
// that would not be filled inside ast_type_replace().
1343-
Py_ssize_t m = PySet_GET_SIZE(expecting);
1344-
if (m > 0) {
1345-
PyObject *names = PyList_New(m);
1346-
if (names == NULL) {
1347-
Py_DECREF(expecting);
1348-
return -1;
1349-
}
1350-
Py_ssize_t i = 0, pos = 0;
1351-
PyObject *item;
1352-
Py_hash_t hash;
1353-
while (_PySet_NextEntry(expecting, &pos, &item, &hash)) {
1354-
PyObject *name = PyObject_Repr(item);
1355-
if (name == NULL) {
1356-
Py_DECREF(expecting);
1357-
Py_DECREF(names);
1358-
return -1;
1359-
}
1360-
// steal the reference 'name'
1361-
PyList_SET_ITEM(names, i++, name);
1362-
}
1363-
Py_DECREF(expecting);
1364-
if (PyList_Sort(names) < 0) {
1365-
Py_DECREF(names);
1366-
return -1;
1367-
}
1368-
PyObject *sep = PyUnicode_FromString(", ");
1369-
if (sep == NULL) {
1370-
Py_DECREF(names);
1371-
return -1;
1372-
}
1373-
PyObject *str_names = PyUnicode_Join(sep, names);
1374-
Py_DECREF(sep);
1375-
Py_DECREF(names);
1376-
if (str_names == NULL) {
1377-
return -1;
1378-
}
1379-
PyErr_Format(PyExc_TypeError,
1380-
"%.400s.__replace__ missing %ld keyword argument%s: %U.",
1381-
Py_TYPE(self)->tp_name, m, m == 1 ? "" : "s", str_names);
1382-
Py_DECREF(str_names);
1383-
return -1;
1384-
}
1385-
else {
1386-
Py_DECREF(expecting);
1387-
return 1;
1388-
}
1389-
}
1390-
13911215
/*
13921216
* Python equivalent:
13931217
*
@@ -1477,9 +1301,6 @@ def visitModule(self, mod):
14771301
if (PyObject_GetOptionalAttr(self, state->__dict__, &dict) < 0) {
14781302
goto cleanup;
14791303
}
1480-
if (ast_type_replace_check(self, dict, fields, attributes, kwargs) < 0) {
1481-
goto cleanup;
1482-
}
14831304
empty_tuple = PyTuple_New(0);
14841305
if (empty_tuple == NULL) {
14851306
goto cleanup;

0 commit comments

Comments
 (0)