Skip to content

Commit 52df518

Browse files
committed
fix: deprecated partial string matches in conversions to enum members, closes #387
1 parent 10d9d2e commit 52df518

File tree

2 files changed

+60
-28
lines changed

2 files changed

+60
-28
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919

2020
### Deprecated
2121

22+
* Functions and methods that take string arguments that represent an underlying
23+
enum in the C core of igraph now print a deprecation warning when provided
24+
with a string that does not match one of the enum member names (as documented
25+
in the docstrings) exactly. Partial matches will be removed in the next
26+
minor or major version, whichever comes first.
27+
2228
* `Graph.to_directed(mutual=...)` is now deprecated, use `mode=...` instead.
2329

2430

src/_igraph/convert.c

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -83,42 +83,68 @@ int PyLong_AsInt(PyObject* obj, int* result) {
8383
int igraphmodule_PyObject_to_enum(PyObject *o,
8484
igraphmodule_enum_translation_table_entry_t* table,
8585
int *result) {
86-
char *s, *s2;
87-
int i, best, best_result, best_unique;
8886

89-
if (o == 0 || o == Py_None)
87+
char *s, *s2;
88+
int i, best, best_result, best_unique;
89+
90+
if (o == 0 || o == Py_None)
91+
return 0;
92+
93+
if (PyLong_Check(o))
94+
return PyLong_AsInt(o, result);
95+
96+
s = PyUnicode_CopyAsString(o);
97+
if (s == 0) {
98+
PyErr_SetString(PyExc_TypeError, "int, long or string expected");
99+
return -1;
100+
}
101+
102+
/* Convert string to lowercase */
103+
for (s2 = s; *s2; s2++) {
104+
*s2 = tolower(*s2);
105+
}
106+
107+
/* Search for matches */
108+
best = 0; best_unique = 0; best_result = -1;
109+
while (table->name != 0) {
110+
if (strcmp(s, table->name) == 0) {
111+
/* Exact match found */
112+
*result = table->value;
113+
free(s);
90114
return 0;
91-
if (PyLong_Check(o))
92-
return PyLong_AsInt(o, result);
93-
s = PyUnicode_CopyAsString(o);
94-
if (s == 0) {
95-
PyErr_SetString(PyExc_TypeError, "int, long or string expected");
96-
return -1;
97115
}
98-
/* Convert string to lowercase */
99-
for (s2=s; *s2; s2++)
100-
*s2 = tolower(*s2);
101-
best = 0; best_unique = 0; best_result = -1;
102-
/* Search for matches */
103-
while (table->name != 0) {
104-
if (strcmp(s, table->name) == 0) {
105-
*result = table->value;
106-
free(s);
107-
return 0;
108-
}
109-
for (i=0; s[i] == table->name[i]; i++);
110-
if (i > best) {
111-
best = i; best_unique = 1; best_result = table->value;
112-
} else if (i == best) best_unique = 0;
113-
table++;
116+
117+
/* Find length of longest prefix that matches */
118+
for (i = 0; s[i] == table->name[i]; i++);
119+
120+
if (i > best) {
121+
/* Found a better match than before */
122+
best = i; best_unique = 1; best_result = table->value;
123+
} else if (i == best) {
124+
/* Best match is not unique */
125+
best_unique = 0;
114126
}
115-
free(s);
116-
if (best_unique) { *result = best_result; return 0; }
127+
128+
table++;
129+
}
130+
131+
free(s);
132+
133+
if (best_unique) {
134+
PyErr_Warn(
135+
PyExc_DeprecationWarning,
136+
"Partial string matches of enum members are deprecated since igraph 0.9.3; "
137+
"use strings that identify an enum member unambiguously."
138+
);
139+
140+
*result = best_result;
141+
return 0;
142+
} else {
117143
PyErr_SetObject(PyExc_ValueError, o);
118144
return -1;
145+
}
119146
}
120147

121-
122148
/**
123149
* \ingroup python_interface_conversion
124150
* \brief Converts a Python object to a corresponding igraph enum, strictly.

0 commit comments

Comments
 (0)