Skip to content

Add container methods to extended dictionaries #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions QuantConnectStubsGenerator.Tests/RendererTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,145 @@ def __isub__(self, item: QuantConnect_Test__EventContainer_Callable) -> typing.S
...
"
}).SetName("GeneratesEventContainerClassForEventsDelegates"),

// GeneratesContainerMethodsForDictionaries
new TestCaseData(
new Dictionary<string, string>()
{
{
"IDictionary.cs",
@"
namespace System.Collections
{
public interface IDictionary : ICollection
{
}

public interface IDictionary : ICollection
{
}
}
"
},
{
"KeyValuePair.cs",
@"
namespace System.Collections.Generic
{
public readonly struct KeyValuePair<TKey, TValue>
{
}
}
"
},
{
"Test.cs",
@"
using System;
using System.Collections;
using System.Collections.Generic;

namespace QuantConnect.Test
{
public class TestDictionary<TKey, TValue> : IDictionary
{
private readonly Dictionary<TValue, TKey> _data = new();

public int Count => _data.Count

public bool ContainsKey(TKey key)
{
return data.ContainsKey(key);
}
}

public class TestDictionary2<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
private readonly List<KeyValuePair<TKey, TValue>> _data = new();

public int Count => _data.Count

public bool ContainsKey(TKey key)
{
return data.ContainsKey(key);
}
}
}"
}
},
new[]
{
@"
from typing import overload
from enum import Enum
import abc

import System.Collections

class IDictionary(System.Collections.ICollection, metaclass=abc.ABCMeta):
""""""This class has no documentation.""""""
",
@"
from typing import overload
from enum import Enum
import typing

import System.Collections.Generic

System_Collections_Generic_KeyValuePair_TKey = typing.TypeVar(""System_Collections_Generic_KeyValuePair_TKey"")
System_Collections_Generic_KeyValuePair_TValue = typing.TypeVar(""System_Collections_Generic_KeyValuePair_TValue"")

class KeyValuePair(typing.Generic[System_Collections_Generic_KeyValuePair_TKey, System_Collections_Generic_KeyValuePair_TValue]):
""""""This class has no documentation.""""""
",
@"
from typing import overload
from enum import Enum
import typing

import QuantConnect.Test
import System
import System.Collections
import System.Collections.Generic

QuantConnect_Test_TestDictionary_TKey = typing.TypeVar(""QuantConnect_Test_TestDictionary_TKey"")
QuantConnect_Test_TestDictionary_TValue = typing.TypeVar(""QuantConnect_Test_TestDictionary_TValue"")
QuantConnect_Test_TestDictionary2_TKey = typing.TypeVar(""QuantConnect_Test_TestDictionary2_TKey"")
QuantConnect_Test_TestDictionary2_TValue = typing.TypeVar(""QuantConnect_Test_TestDictionary2_TValue"")

class TestDictionary(typing.Generic[QuantConnect_Test_TestDictionary_TKey, QuantConnect_Test_TestDictionary_TValue], System.Object, System.Collections.IDictionary):
""""""This class has no documentation.""""""

@property
def count(self) -> int:
...

def __contains__(self, key: QuantConnect_Test_TestDictionary_TKey) -> bool:
...

def __len__(self) -> int:
...

def contains_key(self, key: QuantConnect_Test_TestDictionary_TKey) -> bool:
...

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]]):
""""""This class has no documentation.""""""

@property
def count(self) -> int:
...

def __contains__(self, key: QuantConnect_Test_TestDictionary2_TKey) -> bool:
...

def __len__(self) -> int:
...

def contains_key(self, key: QuantConnect_Test_TestDictionary2_TKey) -> bool:
...
"
}).SetName("GeneratesContainerMethodsForDictionaries"),
};

private class TestGenerator : Generator
Expand Down
19 changes: 19 additions & 0 deletions QuantConnectStubsGenerator/Parser/MethodParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ private void VisitMethod(
_currentClass.Methods.Add(method);

ImprovePythonAccessorIfNecessary(method);
ImproveDictionaryDefinitionIfNecessary(node, method);
}

private Parameter ParseParameter(ParameterSyntax syntax, bool avoidImplicitConversionTypes)
Expand Down Expand Up @@ -405,6 +406,11 @@ private void AddContainerMethodsIfNecessary(MethodDeclarationSyntax node)
return;
}

AddContainerMethods(node);
}

private void AddContainerMethods(MethodDeclarationSyntax node)
{
VisitMethod(node, "__contains__", node.ParameterList.Parameters, _typeConverter.GetType(node.ReturnType, skipTypeNormalization: SkipTypeNormalization), null, false);

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

private void ImproveDictionaryDefinitionIfNecessary(MemberDeclarationSyntax node, Method method)
{
// We add container methods to classes that implement Count and ContainsKey
// because Python.Net does this to add support for operators like 'in' to work with these types
if (node is MethodDeclarationSyntax methodNode &&
method.Name == "ContainsKey" &&
method.Parameters.Count == 1 &&
_currentClass.Properties.Any(property => property.Name == "Count"))
{
AddContainerMethods(methodNode);
}
}

/// <summary>
/// Returns whether the provided documentation string suggests that a certain type is a pandas DataFrame.
/// </summary>
Expand Down
Loading