Skip to content

Commit 48ae5ad

Browse files
authored
Add container methods to extended dictionaries (#67)
* Add container methods to classes that extend IDictionary or IEnumerable<KeyValuePair<TKey, TValue>> * Minor change
1 parent bc14b9b commit 48ae5ad

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

QuantConnectStubsGenerator.Tests/RendererTests.cs

+139
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,145 @@ def __isub__(self, item: QuantConnect_Test__EventContainer_Callable) -> typing.S
366366
...
367367
"
368368
}).SetName("GeneratesEventContainerClassForEventsDelegates"),
369+
370+
// GeneratesContainerMethodsForDictionaries
371+
new TestCaseData(
372+
new Dictionary<string, string>()
373+
{
374+
{
375+
"IDictionary.cs",
376+
@"
377+
namespace System.Collections
378+
{
379+
public interface IDictionary : ICollection
380+
{
381+
}
382+
383+
public interface IDictionary : ICollection
384+
{
385+
}
386+
}
387+
"
388+
},
389+
{
390+
"KeyValuePair.cs",
391+
@"
392+
namespace System.Collections.Generic
393+
{
394+
public readonly struct KeyValuePair<TKey, TValue>
395+
{
396+
}
397+
}
398+
"
399+
},
400+
{
401+
"Test.cs",
402+
@"
403+
using System;
404+
using System.Collections;
405+
using System.Collections.Generic;
406+
407+
namespace QuantConnect.Test
408+
{
409+
public class TestDictionary<TKey, TValue> : IDictionary
410+
{
411+
private readonly Dictionary<TValue, TKey> _data = new();
412+
413+
public int Count => _data.Count
414+
415+
public bool ContainsKey(TKey key)
416+
{
417+
return data.ContainsKey(key);
418+
}
419+
}
420+
421+
public class TestDictionary2<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
422+
{
423+
private readonly List<KeyValuePair<TKey, TValue>> _data = new();
424+
425+
public int Count => _data.Count
426+
427+
public bool ContainsKey(TKey key)
428+
{
429+
return data.ContainsKey(key);
430+
}
431+
}
432+
}"
433+
}
434+
},
435+
new[]
436+
{
437+
@"
438+
from typing import overload
439+
from enum import Enum
440+
import abc
441+
442+
import System.Collections
443+
444+
class IDictionary(System.Collections.ICollection, metaclass=abc.ABCMeta):
445+
""""""This class has no documentation.""""""
446+
",
447+
@"
448+
from typing import overload
449+
from enum import Enum
450+
import typing
451+
452+
import System.Collections.Generic
453+
454+
System_Collections_Generic_KeyValuePair_TKey = typing.TypeVar(""System_Collections_Generic_KeyValuePair_TKey"")
455+
System_Collections_Generic_KeyValuePair_TValue = typing.TypeVar(""System_Collections_Generic_KeyValuePair_TValue"")
456+
457+
class KeyValuePair(typing.Generic[System_Collections_Generic_KeyValuePair_TKey, System_Collections_Generic_KeyValuePair_TValue]):
458+
""""""This class has no documentation.""""""
459+
",
460+
@"
461+
from typing import overload
462+
from enum import Enum
463+
import typing
464+
465+
import QuantConnect.Test
466+
import System
467+
import System.Collections
468+
import System.Collections.Generic
469+
470+
QuantConnect_Test_TestDictionary_TKey = typing.TypeVar(""QuantConnect_Test_TestDictionary_TKey"")
471+
QuantConnect_Test_TestDictionary_TValue = typing.TypeVar(""QuantConnect_Test_TestDictionary_TValue"")
472+
QuantConnect_Test_TestDictionary2_TKey = typing.TypeVar(""QuantConnect_Test_TestDictionary2_TKey"")
473+
QuantConnect_Test_TestDictionary2_TValue = typing.TypeVar(""QuantConnect_Test_TestDictionary2_TValue"")
474+
475+
class TestDictionary(typing.Generic[QuantConnect_Test_TestDictionary_TKey, QuantConnect_Test_TestDictionary_TValue], System.Object, System.Collections.IDictionary):
476+
""""""This class has no documentation.""""""
477+
478+
@property
479+
def count(self) -> int:
480+
...
481+
482+
def __contains__(self, key: QuantConnect_Test_TestDictionary_TKey) -> bool:
483+
...
484+
485+
def __len__(self) -> int:
486+
...
487+
488+
def contains_key(self, key: QuantConnect_Test_TestDictionary_TKey) -> bool:
489+
...
490+
491+
class TestDictionary2(typing.Generic[QuantConnect_Test_TestDictionary2_TKey, QuantConnect_Test_TestDictionary2_TValue], System.Object, typing.Iterable[System.Collections.Generic.KeyValuePair[QuantConnect_Test_TestDictionary2_TKey, QuantConnect_Test_TestDictionary2_TValue]]):
492+
""""""This class has no documentation.""""""
493+
494+
@property
495+
def count(self) -> int:
496+
...
497+
498+
def __contains__(self, key: QuantConnect_Test_TestDictionary2_TKey) -> bool:
499+
...
500+
501+
def __len__(self) -> int:
502+
...
503+
504+
def contains_key(self, key: QuantConnect_Test_TestDictionary2_TKey) -> bool:
505+
...
506+
"
507+
}).SetName("GeneratesContainerMethodsForDictionaries"),
369508
};
370509

371510
private class TestGenerator : Generator

QuantConnectStubsGenerator/Parser/MethodParser.cs

+19
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ private void VisitMethod(
280280
_currentClass.Methods.Add(method);
281281

282282
ImprovePythonAccessorIfNecessary(method);
283+
ImproveDictionaryDefinitionIfNecessary(node, method);
283284
}
284285

285286
private Parameter ParseParameter(ParameterSyntax syntax, bool avoidImplicitConversionTypes)
@@ -405,6 +406,11 @@ private void AddContainerMethodsIfNecessary(MethodDeclarationSyntax node)
405406
return;
406407
}
407408

409+
AddContainerMethods(node);
410+
}
411+
412+
private void AddContainerMethods(MethodDeclarationSyntax node)
413+
{
408414
VisitMethod(node, "__contains__", node.ParameterList.Parameters, _typeConverter.GetType(node.ReturnType, skipTypeNormalization: SkipTypeNormalization), null, false);
409415

410416
if (_currentClass.Methods.All(m => m.Name != "__len__"))
@@ -450,6 +456,19 @@ private void ImprovePythonAccessorIfNecessary(Method newMethod)
450456
_currentClass.Methods.Remove(existingMethod);
451457
}
452458

459+
private void ImproveDictionaryDefinitionIfNecessary(MemberDeclarationSyntax node, Method method)
460+
{
461+
// We add container methods to classes that implement Count and ContainsKey
462+
// because Python.Net does this to add support for operators like 'in' to work with these types
463+
if (node is MethodDeclarationSyntax methodNode &&
464+
method.Name == "ContainsKey" &&
465+
method.Parameters.Count == 1 &&
466+
_currentClass.Properties.Any(property => property.Name == "Count"))
467+
{
468+
AddContainerMethods(methodNode);
469+
}
470+
}
471+
453472
/// <summary>
454473
/// Returns whether the provided documentation string suggests that a certain type is a pandas DataFrame.
455474
/// </summary>

0 commit comments

Comments
 (0)