Skip to content
Open
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
16 changes: 15 additions & 1 deletion Common/Data/SubscriptionDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Python.Runtime;
using static QuantConnect.StringExtensions;

namespace QuantConnect.Data
Expand Down Expand Up @@ -79,7 +80,7 @@ public SubscriptionDataSource(string source, SubscriptionTransportMedium transpo
/// <param name="transportMedium">The transport medium to be used to retrieve the subscription's data from the source</param>
/// <param name="format">The format of the data within the source</param>
public SubscriptionDataSource(string source, SubscriptionTransportMedium transportMedium, FileFormat format)
: this(source, transportMedium, format, null)
: this(source, transportMedium, format, (IEnumerable<KeyValuePair<string, string>>)null)
{
}

Expand All @@ -99,6 +100,19 @@ public SubscriptionDataSource(string source, SubscriptionTransportMedium transpo
Headers = headers?.ToList() ?? _empty;
}

/// <summary>
/// Initializes a new instance of the <see cref="SubscriptionDataSource"/> class with <see cref="SubscriptionTransportMedium.Rest"/>
/// including the specified header values as a Python dictionary.
/// </summary>
/// <param name="source">The subscription's data source location</param>
/// <param name="transportMedium">The transport medium to be used to retrieve the subscription's data from the source</param>
/// <param name="format">The format of the data within the source</param>
/// <param name="headers">The Python dictionary containing the headers to be used for this source</param>
public SubscriptionDataSource(string source, SubscriptionTransportMedium transportMedium, FileFormat format, PyObject headers)
: this(source, transportMedium, format, headers == null ? null : headers.ConvertToDictionary<string, string>())
{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the ConvertToDictionary extension method we can use here:

 this(source, transportMedium, format, headers.ConvertToDictionary<string, string>())

By the way, we could use this method in QCAlgorithm.Python.cs#L1479 as well.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in d9548eb. I replaced the custom conversion with headers == null ? null : headers.ConvertToDictionary<string, string>() and updated the invalid-input test assertion to match the shared extension exception message.

}

/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
Expand Down
42 changes: 42 additions & 0 deletions Tests/Common/Data/SubscriptionDataSourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
*/

using NUnit.Framework;
using Python.Runtime;
using QuantConnect.Data;
using System;
using System.Collections.Generic;

namespace QuantConnect.Tests.Common.Data
{
Expand Down Expand Up @@ -47,5 +50,44 @@ public void ComparesNotEqualWithDifferentTransportMedium()
Assert.IsTrue(one != two);
Assert.IsTrue(!one.Equals(two));
}

[Test]
public void SupportsPythonDictionaryHeaders()
{
using (Py.GIL())
{
using var headers = new PyDict();
headers.SetItem("Authorization".ToPython(), "Basic test-token".ToPython());
headers.SetItem("X-Api-Key".ToPython(), "abc123".ToPython());

var dataSource = new SubscriptionDataSource("https://example.com", SubscriptionTransportMedium.RemoteFile, FileFormat.Csv, headers);
CollectionAssert.AreEquivalent(new[]
{
new KeyValuePair<string, string>("Authorization", "Basic test-token"),
new KeyValuePair<string, string>("X-Api-Key", "abc123")
}, dataSource.Headers);
}
}

[Test]
public void SupportsNullPythonDictionaryHeaders()
{
var dataSource = new SubscriptionDataSource("https://example.com", SubscriptionTransportMedium.RemoteFile, FileFormat.Csv, (PyObject)null);
Assert.IsEmpty(dataSource.Headers);
}

[Test]
public void ThrowsForInvalidPythonHeadersType()
{
using (Py.GIL())
{
using var invalidHeaders = "invalid-headers".ToPython();

var exception = Assert.Throws<ArgumentException>(() =>
new SubscriptionDataSource("https://example.com", SubscriptionTransportMedium.RemoteFile, FileFormat.Csv, invalidHeaders));

StringAssert.Contains("ConvertToDictionary cannot be used", exception.Message);
}
}
}
}