Skip to content
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
2 changes: 1 addition & 1 deletion Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="Accord" Version="3.6.0" />
<PackageReference Include="Accord.Fuzzy" Version="3.6.0" />
<PackageReference Include="Accord.MachineLearning" Version="3.6.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="Accord" Version="3.6.0" />
<PackageReference Include="Accord.Math" Version="3.6.0" />
<PackageReference Include="Accord.Statistics" Version="3.6.0" />
Expand Down
2 changes: 1 addition & 1 deletion Algorithm.Python/QuantConnect.Algorithm.Python.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
</ItemGroup>
<ItemGroup>
<Content Include="OptionUniverseFilterGreeksShortcutsRegressionAlgorithm.py" />
Expand Down
2 changes: 1 addition & 1 deletion Algorithm/QuantConnect.Algorithm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="NodaTime" Version="3.0.5" />
Expand Down
2 changes: 1 addition & 1 deletion AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="NodaTime" Version="3.0.5" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 System;
using Python.Runtime;
using QuantConnect.Util;

namespace QuantConnect.Exceptions
{
/// <summary>
/// Interprets TypeError exceptions raised when comparing datetime.datetime and datetime.date objects
/// </summary>
public class DatetimeDateComparisonPythonExceptionInterpreter : PythonExceptionInterpreter
{
/// <summary>
/// Determines the order that an instance of this class should be called
/// </summary>
public override int Order => 0;

/// <summary>
/// Determines if this interpreter should be applied to the specified exception.
/// </summary>
/// <param name="exception">The exception to check</param>
/// <returns>True if the exception can be interpreted, false otherwise</returns>
public override bool CanInterpret(Exception exception)
{
return base.CanInterpret(exception) &&
exception.Message.Contains(Messages.DatetimeDateComparisonPythonExceptionInterpreter.CantCompareExpectedSubstring);
}

/// <summary>
/// Interprets the specified exception into a new exception
/// </summary>
/// <param name="exception">The exception to be interpreted</param>
/// <param name="innerInterpreter">An interpreter that should be applied to the inner exception.</param>
/// <returns>The interpreted exception</returns>
public override Exception Interpret(Exception exception, IExceptionInterpreter innerInterpreter)
{
var pe = (PythonException)exception;

var message = Messages.DatetimeDateComparisonPythonExceptionInterpreter.InvalidDatetimeDateComparison;
message += PythonUtil.PythonExceptionStackParser(pe.StackTrace);

return new Exception(message, pe);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public override Exception Interpret(Exception exception, IExceptionInterpreter i

var types = pe.Message.Split(':')[1].Trim();
var message = Messages.UnsupportedOperandPythonExceptionInterpreter.InvalidObjectTypesForOperation(types);
if (types.Contains("'datetime.datetime'") && types.Contains("'datetime.date'"))
{
message += Messages.UnsupportedOperandPythonExceptionInterpreter.DatetimeDateOperationHint;
}
message += PythonUtil.PythonExceptionStackParser(pe.StackTrace);

return new Exception(message, pe);
Expand Down
24 changes: 24 additions & 0 deletions Common/Messages/Messages.Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,30 @@ public static string InvalidObjectTypesForOperation(string types)
return $@"Trying to perform a summation, subtraction, multiplication or division between {
types} objects throws a TypeError exception. To prevent the exception, ensure that both values share the same type.";
}

/// <summary>
/// Hint appended when the invalid operands are a datetime.datetime and a datetime.date
/// </summary>
public static string DatetimeDateOperationHint =
" To operate between them, use the date part of the datetime value, e.g.: (expiry.date() - today).days or self.time.date().";
}

/// <summary>
/// Provides user-facing messages for the <see cref="Exceptions.DatetimeDateComparisonPythonExceptionInterpreter"/> class and its consumers or related classes
/// </summary>
public static class DatetimeDateComparisonPythonExceptionInterpreter
{
/// <summary>
/// Expected substring of the TypeError raised when comparing a datetime.datetime with a datetime.date
/// </summary>
public static string CantCompareExpectedSubstring = "can't compare datetime.datetime to datetime.date";

/// <summary>
/// User-facing message for datetime.datetime vs datetime.date comparisons
/// </summary>
public static string InvalidDatetimeDateComparison =
"Trying to compare 'datetime.datetime' and 'datetime.date' objects throws a TypeError exception. " +
"To prevent the exception, compare using the date part of the datetime value, e.g.: self.time.date() <= some_date.";
}
}
}
2 changes: 1 addition & 1 deletion Common/QuantConnect.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<Message Text="SelectedOptimization $(SelectedOptimization)" Importance="high" />
</Target>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="CloneExtensions" Version="1.3.0" />
<PackageReference Include="fasterflect" Version="3.0.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
Expand Down
2 changes: 1 addition & 1 deletion Engine/QuantConnect.Lean.Engine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<Message Text="SelectedOptimization $(SelectedOptimization)" Importance="high" />
</Target>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="fasterflect" Version="3.0.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
Expand Down
2 changes: 1 addition & 1 deletion Indicators/QuantConnect.Indicators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<Message Text="SelectedOptimization $(SelectedOptimization)" Importance="high" />
</Target>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Report/QuantConnect.Report.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="Deedle" Version="2.1.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
Expand Down
2 changes: 1 addition & 1 deletion Research/QuantConnect.Research.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<PackageReference Include="Plotly.NET" Version="5.1.0" />
<PackageReference Include="Plotly.NET.CSharp" Version="0.13.0" />
<PackageReference Include="Plotly.NET.Interactive" Version="5.0.0" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="NodaTime" Version="3.0.5" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* 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 NUnit.Framework;
using NUnit.Framework.Constraints;
using Python.Runtime;
using QuantConnect.Exceptions;
using System;
using System.Collections.Generic;

namespace QuantConnect.Tests.Common.Exceptions
{
[TestFixture]
public class DatetimeDateComparisonPythonExceptionInterpreterTests
{
private PythonException _comparisonException;
private PythonException _unsupportedOperandException;

[OneTimeSetUp]
public void Setup()
{
using (Py.GIL())
{
var module = Py.Import("Test_PythonExceptionInterpreter");
dynamic algorithm = module.GetAttr("Test_PythonExceptionInterpreter").Invoke();

try
{
// x = datetime(2020, 1, 2) < date(2020, 1, 1)
algorithm.datetime_date_comparison();
}
catch (PythonException pythonException)
{
_comparisonException = pythonException;
}

try
{
// x = None + "Pepe Grillo"
algorithm.unsupported_operand();
}
catch (PythonException pythonException)
{
_unsupportedOperandException = pythonException;
}
}
}

[Test]
public void StackInterpreterRewritesComparisonErrorWithDateHint()
{
var interpreter = StackExceptionInterpreter.CreateFromAssemblies();
var interpreted = interpreter.Interpret(_comparisonException, NullExceptionInterpreter.Instance);
Assert.IsTrue(interpreted.Message.Contains(".date()"), interpreted.Message);
Assert.IsTrue(interpreted.Message.Contains("x = datetime(2020, 1, 2) < date(2020, 1, 1)"), interpreted.Message);
}

[Test]
[TestCase(typeof(Exception), ExpectedResult = false)]
[TestCase(typeof(KeyNotFoundException), ExpectedResult = false)]
[TestCase(typeof(DivideByZeroException), ExpectedResult = false)]
[TestCase(typeof(InvalidOperationException), ExpectedResult = false)]
[TestCase(typeof(PythonException), ExpectedResult = true)]
public bool CanInterpretReturnsTrueForOnlyDatetimeDateComparisonPythonExceptionType(Type exceptionType)
{
var exception = CreateExceptionFromType(exceptionType);
return new DatetimeDateComparisonPythonExceptionInterpreter().CanInterpret(exception);
}

[Test]
public void CanInterpretReturnsFalseForOtherTypeErrors()
{
Assert.IsFalse(new DatetimeDateComparisonPythonExceptionInterpreter().CanInterpret(_unsupportedOperandException));
Assert.IsFalse(new UnsupportedOperandPythonExceptionInterpreter().CanInterpret(_comparisonException));
}

[Test]
[TestCase(typeof(Exception), true)]
[TestCase(typeof(KeyNotFoundException), true)]
[TestCase(typeof(DivideByZeroException), true)]
[TestCase(typeof(InvalidOperationException), true)]
[TestCase(typeof(PythonException), false)]
public void InterpretThrowsForNonDatetimeDateComparisonPythonExceptionTypes(Type exceptionType, bool expectThrow)
{
var exception = CreateExceptionFromType(exceptionType);
var interpreter = new DatetimeDateComparisonPythonExceptionInterpreter();
var constraint = expectThrow ? (IResolveConstraint)Throws.Exception : Throws.Nothing;
Assert.That(() => interpreter.Interpret(exception, NullExceptionInterpreter.Instance), constraint);
}

[Test]
public void InterpretedMessageContainsGuidanceAndStackInformation()
{
var interpreted = new DatetimeDateComparisonPythonExceptionInterpreter().Interpret(_comparisonException, NullExceptionInterpreter.Instance);
Assert.IsTrue(interpreted.Message.Contains(
Messages.DatetimeDateComparisonPythonExceptionInterpreter.InvalidDatetimeDateComparison), interpreted.Message);
Assert.IsTrue(interpreted.Message.Contains("x = datetime(2020, 1, 2) < date(2020, 1, 1)"), interpreted.Message);
}

private Exception CreateExceptionFromType(Type type) => type == typeof(PythonException) ? _comparisonException : (Exception)Activator.CreateInstance(type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace QuantConnect.Tests.Common.Exceptions
public class UnsupportedOperandPythonExceptionInterpreterTests
{
private PythonException _pythonException;
private PythonException _datetimeDateException;
private PythonException _dateDatetimeException;

[OneTimeSetUp]
public void Setup()
Expand All @@ -44,6 +46,26 @@ public void Setup()
{
_pythonException = pythonException;
}

try
{
// x = datetime(2020, 1, 2) - date(2020, 1, 1)
algorithm.unsupported_operand_datetime_date();
}
catch (PythonException pythonException)
{
_datetimeDateException = pythonException;
}

try
{
// x = date(2020, 1, 1) - datetime(2020, 1, 2)
algorithm.unsupported_operand_date_datetime();
}
catch (PythonException pythonException)
{
_dateDatetimeException = pythonException;
}
}
}

Expand Down Expand Up @@ -73,6 +95,24 @@ public void InterpretThrowsForNonUnsupportedOperandPythonExceptionTypes(Type exc
Assert.That(() => interpreter.Interpret(exception, NullExceptionInterpreter.Instance), constraint);
}

[Test]
[TestCase(true)]
[TestCase(false)]
public void AppendsDateHintForDatetimeDateOperands(bool datetimeFirst)
{
var exception = datetimeFirst ? _datetimeDateException : _dateDatetimeException;
var interpreted = new UnsupportedOperandPythonExceptionInterpreter().Interpret(exception, NullExceptionInterpreter.Instance);
Assert.IsTrue(interpreted.Message.Contains("'datetime.datetime'"), interpreted.Message);
Assert.IsTrue(interpreted.Message.Contains(".date()"), interpreted.Message);
}

[Test]
public void DoesNotAppendDateHintForOtherOperands()
{
var interpreted = new UnsupportedOperandPythonExceptionInterpreter().Interpret(_pythonException, NullExceptionInterpreter.Instance);
Assert.IsFalse(interpreted.Message.Contains(".date()"), interpreted.Message);
}

[Test]
public void VerifyMessageContainsStackTraceInformation()
{
Expand Down
2 changes: 1 addition & 1 deletion Tests/QuantConnect.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.64" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.65" />
<PackageReference Include="Accord" Version="3.6.0" />
<PackageReference Include="Accord.Math" Version="3.6.0" />
<PackageReference Include="Common.Logging" Version="3.4.1" />
Expand Down
9 changes: 9 additions & 0 deletions Tests/RegressionAlgorithms/Test_PythonExceptionInterpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ def no_method_match_rsi(self):
def unsupported_operand(self):
x = None + "Pepe Grillo"

def unsupported_operand_datetime_date(self):
x = datetime(2020, 1, 2) - date(2020, 1, 1)

def unsupported_operand_date_datetime(self):
x = date(2020, 1, 1) - datetime(2020, 1, 2)

def datetime_date_comparison(self):
x = datetime(2020, 1, 2) < date(2020, 1, 1)

def module_not_found(self):
from MissingClrNamespace.Distributions import Normal

Expand Down
Loading