/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using Python.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Interfaces;
using System.Collections;
using QuantConnect.Python;
namespace QuantConnect
{
///
/// Provides a base class for types holding key value pairs with helper methods for easy usage in Python
///
[PandasNonExpandable]
#pragma warning disable CA1708 // Identifiers should differ by more than case
public abstract class ExtendedDictionary : IExtendedDictionary
#pragma warning restore CA1708 // Identifiers should differ by more than case
{
///
/// Gets the number of elements contained in the dictionary
///
public abstract int Count { get; }
///
/// Removes all items from the .
///
/// The is read-only.
public virtual void Clear()
{
if (IsReadOnly)
{
throw new InvalidOperationException(Messages.ExtendedDictionary.ClearInvalidOperation(this));
}
throw new NotImplementedException(Messages.ExtendedDictionary.ClearMethodNotImplemented);
}
///
/// Gets the value associated with the specified key.
///
///
/// true if the object that implements contains an element with the specified key; otherwise, false.
///
/// The key whose value to get.
/// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized.
/// is null.
public abstract bool TryGetValue(TKey key, out TValue value);
///
/// Checks if the dictionary contains the specified key.
///
/// The key to locate in the dictionary
/// true if the dictionary contains an element with the specified key; otherwise, false.
public virtual bool ContainsKey(TKey key)
{
return TryGetValue(key, out _);
}
///
/// Gets all the items in the dictionary
///
/// All the items in the dictionary
public abstract IEnumerable> GetItems();
///
/// Gets an containing the key objects of the .
///
///
/// A containing the key objects of the object that implements .
///
protected abstract IEnumerable GetKeys { get; }
///
/// Gets an containing the values in the .
///
///
/// An containing the values in the object that implements .
///
protected abstract IEnumerable GetValues { get; }
///
/// Gets a value indicating whether the object is read-only.
///
/// IDictionary implementation
public virtual bool IsReadOnly => true;
///
/// Removes the value with the specified key
///
/// The key object of the element to remove.
/// true if the element is successfully found and removed; otherwise, false.
public virtual bool Remove(TKey key)
{
if (IsReadOnly)
{
throw new InvalidOperationException(Messages.ExtendedDictionary.RemoveInvalidOperation(this));
}
throw new NotImplementedException(Messages.ExtendedDictionary.RemoveMethodNotImplemented);
}
///
/// Indexer method for the base dictioanry to access the objects by their symbol.
///
/// IDictionary implementation
/// Key object indexer
/// Object of
public abstract TValue this[TKey key] { get; set; }
///
/// Removes all keys and values from the .
///
public void clear()
{
Clear();
}
///
/// Creates a shallow copy of the .
///
/// Returns a shallow copy of the dictionary. It doesn't modify the original dictionary.
public PyDict copy()
{
return fromkeys(GetKeys.ToArray());
}
///
/// Creates a new dictionary from the given sequence of elements.
///
/// Sequence of elements which is to be used as keys for the new dictionary
/// Returns a new dictionary with the given sequence of elements as the keys of the dictionary.
public PyDict fromkeys(TKey[] sequence)
{
return fromkeys(sequence, default);
}
///
/// Creates a new dictionary from the given sequence of elements with a value provided by the user.
///
/// Sequence of elements which is to be used as keys for the new dictionary
/// Value which is set to each each element of the dictionary
/// Returns a new dictionary with the given sequence of elements as the keys of the dictionary.
/// Each element of the newly created dictionary is set to the provided value.
public PyDict fromkeys(TKey[] sequence, TValue value)
{
using (Py.GIL())
{
var dict = new PyDict();
foreach (var key in sequence)
{
var pyValue = get(key, value);
dict.SetItem(key.ToPython(), pyValue.ToPython());
}
return dict;
}
}
///
/// Returns the value for the specified key if key is in dictionary.
///
/// key to be searched in the dictionary
/// The value for the specified key if key is in dictionary.
/// None if the key is not found and value is not specified.
public TValue get(TKey key)
{
TValue data;
TryGetValue(key, out data);
return data;
}
///
/// Returns the value for the specified key if key is in dictionary.
///
/// key to be searched in the dictionary
/// Value to be returned if the key is not found. The default value is null.
/// The value for the specified key if key is in dictionary.
/// value if the key is not found and value is specified.
public TValue get(TKey key, TValue value)
{
TValue data;
if (TryGetValue(key, out data))
{
return data;
}
return value;
}
///
/// Returns a view object that displays a list of dictionary's (key, value) tuple pairs.
///
/// Returns a view object that displays a list of a given dictionary's (key, value) tuple pair.
public PyList items()
{
using (Py.GIL())
{
var pyList = new PyList();
foreach (var (key, value) in GetItems())
{
using var pyKey = key.ToPython();
using var pyValue = value.ToPython();
using var pyKvp = new PyTuple([pyKey, pyValue]);
pyList.Append(pyKvp);
}
return pyList;
}
}
///
/// Returns and removes an arbitrary element (key, value) pair from the dictionary.
///
/// Returns an arbitrary element (key, value) pair from the dictionary
/// removes an arbitrary element(the same element which is returned) from the dictionary.
/// Note: Arbitrary elements and random elements are not same.The popitem() doesn't return a random element.
public PyTuple popitem()
{
throw new NotSupportedException(Messages.ExtendedDictionary.PopitemMethodNotSupported(this));
}
///
/// Returns the value of a key (if the key is in dictionary). If not, it inserts key with a value to the dictionary.
///
/// Key with null/None value is inserted to the dictionary if key is not in the dictionary.
/// The value of the key if it is in the dictionary
/// None if key is not in the dictionary
public TValue setdefault(TKey key)
{
return setdefault(key, default);
}
///
/// Returns the value of a key (if the key is in dictionary). If not, it inserts key with a value to the dictionary.
///
/// Key with a value default_value is inserted to the dictionary if key is not in the dictionary.
/// Default value
/// The value of the key if it is in the dictionary
/// default_value if key is not in the dictionary and default_value is specified
public TValue setdefault(TKey key, TValue default_value)
{
TValue data;
if (TryGetValue(key, out data))
{
return data;
}
if (IsReadOnly)
{
throw new KeyNotFoundException(Messages.ExtendedDictionary.KeyNotFoundDueToNoData(this, key));
}
this[key] = default_value;
return default_value;
}
///
/// Removes and returns an element from a dictionary having the given key.
///
/// Key which is to be searched for removal
/// If key is found - removed/popped element from the dictionary
/// If key is not found - KeyError exception is raised
public TValue pop(TKey key)
{
return pop(key, default);
}
///
/// Removes and returns an element from a dictionary having the given key.
///
/// Key which is to be searched for removal
/// Value which is to be returned when the key is not in the dictionary
/// If key is found - removed/popped element from the dictionary
/// If key is not found - value specified as the second argument(default)
public TValue pop(TKey key, TValue default_value)
{
TValue data;
if (TryGetValue(key, out data))
{
Remove(key);
return data;
}
return default_value;
}
///
/// Updates the dictionary with the elements from the another dictionary object or from an iterable of key/value pairs.
/// The update() method adds element(s) to the dictionary if the key is not in the dictionary.If the key is in the dictionary, it updates the key with the new value.
///
/// Takes either a dictionary or an iterable object of key/value pairs (generally tuples).
public void update(PyObject other)
{
if (IsReadOnly)
{
throw new InvalidOperationException(Messages.ExtendedDictionary.UpdateInvalidOperation(this));
}
var dictionary = other.ConvertToDictionary();
foreach (var kvp in dictionary)
{
this[kvp.Key] = kvp.Value;
}
}
///
/// Returns a view object that displays a list of all the key objects in the dictionary
///
/// Returns a view object that displays a list of all the key objects.
/// When the dictionary is changed, the view object also reflect these changes.
public PyList keys()
{
return GetKeys.ToPyList();
}
///
/// Returns a view object that displays a list of all the values in the dictionary.
///
/// Returns a view object that displays a list of all values in a given dictionary.
public PyList values()
{
return GetValues.ToPyList();
}
///
/// Checks if the symbol is implicitly created from a string, in which case it is not in the symbol cache,
/// and throws a KeyNotFoundException.
///
protected void CheckForImplicitlyCreatedSymbol(Symbol symbol)
{
if (symbol.ID == new SecurityIdentifier(symbol.ID.Symbol, 0))
{
throw new KeyNotFoundException(Messages.ExtendedDictionary.TickerNotFoundInSymbolCache(symbol.ID.Symbol));
}
}
}
}