Skip to content

Commit d7b8bcd

Browse files
authored
Enable some functools tests (#968)
1 parent 381444e commit d7b8bcd

File tree

4 files changed

+59
-40
lines changed

4 files changed

+59
-40
lines changed

Src/IronPython.Modules/_functools.cs

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,11 @@ public class partial : IWeakReferenceable {
7474
private const string _defaultDoc = "partial(func, *args, **keywords) - new function with partial application\n of the given arguments and keywords.\n";
7575

7676
private readonly CodeContext/*!*/ _context; // code context from the caller who created us
77-
private readonly object/*!*/ _function; // the callable function to dispatch to
78-
private readonly object[]/*!*/ _args; // the initially provided arguments
79-
private readonly IDictionary<object, object> _keywordArgs; // the initially provided keyword arguments or null
77+
private object?[]/*!*/ _args; // the initially provided arguments
78+
private IDictionary<object, object?> _keywordArgs; // the initially provided keyword arguments
8079

81-
private CallSite<Func<CallSite, CodeContext, object, object[], IDictionary<object, object>, object>>? _dictSite; // the dictionary call site if ever called w/ keyword args
82-
private CallSite<Func<CallSite, CodeContext, object, object[], object>>? _splatSite; // the position only call site
80+
private CallSite<Func<CallSite, CodeContext, object, object?[], IDictionary<object, object?>, object>>? _dictSite; // the dictionary call site if ever called w/ keyword args
81+
private CallSite<Func<CallSite, CodeContext, object, object?[], object>>? _splatSite; // the position only call site
8382
private PythonDictionary? _dict; // dictionary for storing extra attributes
8483
private WeakRefTracker? _tracker; // tracker so users can use Python weak references
8584
private string? _doc; // A custom docstring, if used
@@ -101,8 +100,8 @@ public partial(CodeContext/*!*/ context, object? func, [NotNull, ParamDictionary
101100
throw PythonOps.TypeError("the first argument must be callable");
102101
}
103102

104-
_function = func;
105-
_keywordArgs = keywords;
103+
this.func = func;
104+
_keywordArgs = new PythonDictionary(keywords);
106105
_args = args;
107106
_context = context;
108107
}
@@ -122,25 +121,17 @@ public static void Set__doc__([NotNull]partial self, object? value) {
122121
}
123122

124123
/// <summary>
125-
/// Gets the function which will be called
124+
/// Gets the function which will be called.
126125
/// </summary>
127-
public object func {
128-
get {
129-
return _function;
130-
}
131-
}
126+
public object func { get; private set; }
132127

133128
/// <summary>
134129
/// Gets the initially provided positional arguments.
135130
/// </summary>
136-
public object args {
137-
get {
138-
return PythonTuple.MakeTuple(_args);
139-
}
140-
}
131+
public object args => PythonTuple.MakeTuple(_args);
141132

142133
/// <summary>
143-
/// Gets the initially provided keyword arguments or None.
134+
/// Gets the initially provided keyword arguments.
144135
/// </summary>
145136
public object keywords {
146137
get {
@@ -178,11 +169,28 @@ public PythonTuple __reduce__() {
178169
return PythonTuple.MakeTuple(
179170
DynamicHelpers.GetPythonTypeFromType(typeof(partial)),
180171
PythonTuple.MakeTuple(func),
181-
PythonTuple.MakeTuple(func, args, keywords),
182-
null // TODO: what should this be?
172+
PythonTuple.MakeTuple(func, args, keywords, __dict__)
183173
);
184174
}
185175

176+
public void __setstate__(CodeContext context, [NotNull] PythonTuple state) {
177+
if (state.Count == 4
178+
&& PythonOps.IsCallable(context, state[0])
179+
&& state[1] is PythonTuple args
180+
&& state[2] is PythonDictionary keywords) {
181+
func = state[0]!;
182+
_args = args._data;
183+
_keywordArgs = keywords;
184+
if (state[3] is PythonDictionary dict)
185+
__dict__.update(context, dict);
186+
else if (!(state[3] is null)) {
187+
throw PythonOps.TypeError("invalid partial state");
188+
}
189+
} else {
190+
throw PythonOps.TypeError("invalid partial state");
191+
}
192+
}
193+
186194
#endregion
187195

188196
#region Operator methods
@@ -191,23 +199,23 @@ public PythonTuple __reduce__() {
191199
/// Calls func with the previously provided arguments and more positional arguments.
192200
/// </summary>
193201
[SpecialName]
194-
public object? Call(CodeContext/*!*/ context, [NotNull]params object[] args) {
202+
public object? Call(CodeContext/*!*/ context, [NotNull]params object?[] args) {
195203
if (_keywordArgs == null) {
196204
EnsureSplatSite();
197-
return _splatSite!.Target(_splatSite, context, _function, ArrayUtils.AppendRange(_args, args));
205+
return _splatSite!.Target(_splatSite, context, func, ArrayUtils.AppendRange(_args, args));
198206
}
199207

200208
EnsureDictSplatSite();
201-
return _dictSite!.Target(_dictSite, context, _function, ArrayUtils.AppendRange(_args, args), _keywordArgs);
209+
return _dictSite!.Target(_dictSite, context, func, ArrayUtils.AppendRange(_args, args), _keywordArgs);
202210
}
203211

204212
/// <summary>
205213
/// Calls func with the previously provided arguments and more positional arguments and keyword arguments.
206214
/// </summary>
207215
[SpecialName]
208-
public object? Call(CodeContext/*!*/ context, [ParamDictionary, NotNull]IDictionary<object, object> dict, [NotNull]params object[] args) {
216+
public object? Call(CodeContext/*!*/ context, [ParamDictionary, NotNull]IDictionary<object, object?> dict, [NotNull]params object?[] args) {
209217

210-
IDictionary<object, object> finalDict;
218+
IDictionary<object, object?> finalDict;
211219
if (_keywordArgs != null) {
212220
PythonDictionary pd = new PythonDictionary();
213221
pd.update(context, _keywordArgs);
@@ -219,7 +227,7 @@ public PythonTuple __reduce__() {
219227
}
220228

221229
EnsureDictSplatSite();
222-
return _dictSite!.Target(_dictSite, context, _function, ArrayUtils.AppendRange(_args, args), finalDict);
230+
return _dictSite!.Target(_dictSite, context, func, ArrayUtils.AppendRange(_args, args), finalDict);
223231
}
224232

225233
/// <summary>
@@ -267,7 +275,7 @@ private void EnsureSplatSite() {
267275
if (_splatSite == null) {
268276
Interlocked.CompareExchange(
269277
ref _splatSite,
270-
CallSite<Func<CallSite, CodeContext, object, object[], object>>.Create(
278+
CallSite<Func<CallSite, CodeContext, object, object?[], object>>.Create(
271279
Binders.InvokeSplat(_context.LanguageContext)
272280
),
273281
null
@@ -279,7 +287,7 @@ private void EnsureDictSplatSite() {
279287
if (_dictSite == null) {
280288
Interlocked.CompareExchange(
281289
ref _dictSite,
282-
CallSite<Func<CallSite, CodeContext, object, object[], IDictionary<object, object>, object>>.Create(
290+
CallSite<Func<CallSite, CodeContext, object, object?[], IDictionary<object, object?>, object>>.Create(
283291
Binders.InvokeKeywords(_context.LanguageContext)
284292
),
285293
null

Src/IronPython/Runtime/PythonDictionary.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ internal PythonDictionary(IDictionary dict) {
4949
_storage = storage;
5050
}
5151

52+
internal PythonDictionary(IDictionary<object, object> dict) {
53+
var storage = new CommonDictionaryStorage();
54+
55+
foreach (var pair in dict) {
56+
storage.AddNoLock(pair.Key, pair.Value);
57+
}
58+
_storage = storage;
59+
}
60+
5261
internal PythonDictionary(PythonDictionary dict) {
5362
_storage = dict._storage.Clone();
5463
}

Src/StdLib/Lib/test/test_functools.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import abc
22
import collections
3+
import gc
34
from itertools import permutations
45
import pickle
56
from random import choice
@@ -124,6 +125,7 @@ def test_weakref(self):
124125
p = proxy(f)
125126
self.assertEqual(f.func, p.func)
126127
f = None
128+
gc.collect() # required by IronPython
127129
self.assertRaises(ReferenceError, getattr, p, 'func')
128130

129131
def test_with_bound_and_unbound_methods(self):

Tests/test_functools_stdlib.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def load_tests(loader, standard_tests, pattern):
1717
if sys.implementation.name == 'ironpython':
1818
suite = unittest.TestSuite()
1919
#suite.addTest(test.test_functools.TestCmpToKeyC('test_bad_cmp')) # TypeError: cmp_to_key() takes exactly 1 argument (2 given)
20-
#uite.addTest(test.test_functools.TestCmpToKeyC('test_cmp_to_key')) # TypeError: cmp_to_key() takes exactly 1 argument (2 given)
20+
#suite.addTest(test.test_functools.TestCmpToKeyC('test_cmp_to_key')) # TypeError: cmp_to_key() takes exactly 1 argument (2 given)
2121
#suite.addTest(test.test_functools.TestCmpToKeyC('test_cmp_to_key_arguments')) # TypeError: cmp_to_key() takes exactly 1 argument (2 given)
2222
#suite.addTest(test.test_functools.TestCmpToKeyC('test_hash')) # TypeError: cmp_to_key() takes exactly 1 argument (2 given)
2323
#suite.addTest(test.test_functools.TestCmpToKeyC('test_obj_field')) # TypeError: cmp_to_key() takes exactly 1 argument (2 given)
@@ -47,12 +47,12 @@ def load_tests(loader, standard_tests, pattern):
4747
suite.addTest(test.test_functools.TestPartialC('test_keyword'))
4848
suite.addTest(test.test_functools.TestPartialC('test_kw_combinations'))
4949
suite.addTest(test.test_functools.TestPartialC('test_no_side_effects'))
50-
#suite.addTest(test.test_functools.TestPartialC('test_pickle')) # StackOverflowException
50+
suite.addTest(test.test_functools.TestPartialC('test_pickle'))
5151
suite.addTest(test.test_functools.TestPartialC('test_positional'))
5252
suite.addTest(test.test_functools.TestPartialC('test_protection_of_callers_dict_argument'))
5353
#suite.addTest(test.test_functools.TestPartialC('test_repr')) # AssertionError
5454
#suite.addTest(test.test_functools.TestPartialC('test_setstate_refcount')) # AttributeError: 'partial' object has no attribute '__setstate__'
55-
#suite.addTest(test.test_functools.TestPartialC('test_weakref')) # AssertionError: ReferenceError not raised by getattr
55+
suite.addTest(test.test_functools.TestPartialC('test_weakref'))
5656
suite.addTest(test.test_functools.TestPartialC('test_with_bound_and_unbound_methods'))
5757
suite.addTest(test.test_functools.TestPartialCSubclass('test_arg_combinations'))
5858
suite.addTest(test.test_functools.TestPartialCSubclass('test_argument_checking'))
@@ -63,12 +63,12 @@ def load_tests(loader, standard_tests, pattern):
6363
suite.addTest(test.test_functools.TestPartialCSubclass('test_keyword'))
6464
suite.addTest(test.test_functools.TestPartialCSubclass('test_kw_combinations'))
6565
suite.addTest(test.test_functools.TestPartialCSubclass('test_no_side_effects'))
66-
#suite.addTest(test.test_functools.TestPartialCSubclass('test_pickle')) # StackOverflowException
66+
suite.addTest(test.test_functools.TestPartialCSubclass('test_pickle'))
6767
suite.addTest(test.test_functools.TestPartialCSubclass('test_positional'))
6868
suite.addTest(test.test_functools.TestPartialCSubclass('test_protection_of_callers_dict_argument'))
6969
#suite.addTest(test.test_functools.TestPartialCSubclass('test_repr')) # AssertionError
7070
#suite.addTest(test.test_functools.TestPartialCSubclass('test_setstate_refcount')) # AttributeError: 'PartialSubclass' object has no attribute '__setstate__'
71-
#suite.addTest(test.test_functools.TestPartialCSubclass('test_weakref')) # AssertionError: ReferenceError not raised by getattr
71+
suite.addTest(test.test_functools.TestPartialCSubclass('test_weakref'))
7272
suite.addTest(test.test_functools.TestPartialCSubclass('test_with_bound_and_unbound_methods'))
7373
suite.addTest(test.test_functools.TestPartialMethod('test_abstract'))
7474
suite.addTest(test.test_functools.TestPartialMethod('test_arg_combinations'))
@@ -90,7 +90,7 @@ def load_tests(loader, standard_tests, pattern):
9090
suite.addTest(test.test_functools.TestPartialPy('test_no_side_effects'))
9191
suite.addTest(test.test_functools.TestPartialPy('test_positional'))
9292
suite.addTest(test.test_functools.TestPartialPy('test_protection_of_callers_dict_argument'))
93-
#suite.addTest(test.test_functools.TestPartialPy('test_weakref')) # AssertionError: ReferenceError not raised by getattr
93+
suite.addTest(test.test_functools.TestPartialPy('test_weakref'))
9494
suite.addTest(test.test_functools.TestPartialPy('test_with_bound_and_unbound_methods'))
9595
suite.addTest(test.test_functools.TestReduce('test_iterator_usage'))
9696
suite.addTest(test.test_functools.TestReduce('test_reduce'))
@@ -105,11 +105,11 @@ def load_tests(loader, standard_tests, pattern):
105105
suite.addTest(test.test_functools.TestSingleDispatch('test_register_decorator'))
106106
suite.addTest(test.test_functools.TestSingleDispatch('test_simple_overloads'))
107107
suite.addTest(test.test_functools.TestSingleDispatch('test_wrapping_attributes'))
108-
#suite.addTest(test.test_functools.TestTotalOrdering('test_no_operations_defined')) # AssertionError: ValueError not raised
109-
#suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_ge')) # TypeError: unsupported operand type(s) for <: 'A' and 'A'
110-
#suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_gt')) # TypeError: unsupported operand type(s) for <=: 'A' and 'A'
111-
#suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_le')) # TypeError: unsupported operand type(s) for <: 'A' and 'A'
112-
#suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_lt')) # TypeError: unsupported operand type(s) for <=: 'A' and 'A'
108+
suite.addTest(test.test_functools.TestTotalOrdering('test_no_operations_defined'))
109+
suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_ge'))
110+
suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_gt'))
111+
suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_le'))
112+
suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_lt'))
113113
#suite.addTest(test.test_functools.TestTotalOrdering('test_total_ordering_no_overwrite')) # ValueError: must define at least one ordering operation: < > <= >=
114114
suite.addTest(test.test_functools.TestTotalOrdering('test_type_error_when_not_implemented'))
115115
suite.addTest(test.test_functools.TestUpdateWrapper('test_builtin_update'))

0 commit comments

Comments
 (0)