From Steve.Luke at moldev.com Wed Sep 19 12:00:11 2018 From: Steve.Luke at moldev.com (Luke, Steve) Date: Wed, 19 Sep 2018 16:00:11 +0000 Subject: [Python.NET] Using PyScope to set variables I can use in imported scripts Message-ID: I am new to Python.Net and trying to convert an application that used IronPython to embed a Python environment into a .Net application and allow the user to run Python scripts inside our application. The part that embeds the Python environment is VB.Net (but could be C# if that would be better). I am running into problems setting variables in the scope such that they can be used in the Python scripts we call. I keep getting an error: NameError: global name 'Bridge' is not defined. This is the VB code I am trying to use: PythonEngine.Initialize() Using (Py.GIL()) Dim moduleName As String = "PrintToBridge" Dim pythonCommandLine As String = "Not used yet" Dim scope As PyScope = Py.CreateScope() Try scope.Set("Bridge", bridge) scope.Set("SetupParams", cmdLine) Dim script As Object = scope.Import(moduleName) bridge.mm.PrintMsg("Scope has Bridge:" + Str(scope.Contains("Bridge"))) script.Startup(pythonCommandLine) Finally scope.Dispose() End Try End Using The Python script: def Startup(param): Bridge.mm.PrintMsg('Startup called') def Docommand(param): Bridge.mm.PrintMsg('docommand called') def Shutdown(): Bridge.mm.PrintMsg('Shutdown called') How can I set a variable such that it would be recognized by the called Python script? Importing the script works, I can force python-y things to happen. And the check to see if the object I want is in scope returns true. But it still isn't available in Python. I have tried scope.Eval(moduleName + ".Startup('" + pythonCommandLine + "')") and Exec() with locals: Dim locals As PyDict = New PyDict() locals.SetItem("Bridge".ToPython(), bridge.ToPython()) scope.Exec(moduleName + ".Startup('" + pythonCommandLine + "')", locals) All with the same effect. Any ideas on how to get set variables so scripts can gain access to them? Thanks, Steve Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment. -------------- next part -------------- An HTML attachment was scrubbed... URL: From denis.akhiyarov at gmail.com Mon Sep 24 09:36:19 2018 From: denis.akhiyarov at gmail.com (Denis Akhiyarov) Date: Mon, 24 Sep 2018 08:36:19 -0500 Subject: [Python.NET] Using PyScope to set variables I can use in imported scripts In-Reply-To: References: Message-ID: I copied Wenguang Yang (@yagweb), who developed PyScope. Steve, did you look at the unit tests for PyScope? Thanks, Denis On Mon, Sep 24, 2018 at 8:28 AM Luke, Steve wrote: > I am new to Python.Net and trying to convert an application that used > IronPython to embed a Python environment into a .Net application and allow > the user to run Python scripts inside our application. The part that > embeds the Python environment is VB.Net (but could be C# if that would be > better). > > > I am running into problems setting variables in the scope such that they > can be used in the Python scripts we call. I keep getting an error: > NameError: global name 'Bridge' is not defined. > > > This is the VB code I am trying to use: > > PythonEngine.Initialize() > > Using (Py.GIL()) > Dim moduleName As String = "PrintToBridge" > Dim pythonCommandLine As String = "Not used yet" > > Dim scope As PyScope = Py.CreateScope() > Try > scope.Set("Bridge", bridge) > scope.Set("SetupParams", cmdLine) > > Dim script As Object = scope.Import(moduleName) > bridge.mm.PrintMsg("Scope has Bridge:" + > Str(scope.Contains("Bridge"))) > script.Startup(pythonCommandLine) > Finally > scope.Dispose() > End Try > End Using > > The Python script: > > def Startup(param): > Bridge.mm.PrintMsg('Startup called') > > def Docommand(param): > Bridge.mm.PrintMsg('docommand called') > > def Shutdown(): > Bridge.mm.PrintMsg('Shutdown called') > > > How can I set a variable such that it would be recognized by the called > Python script? Importing the script works, I can force python-y things to > happen. And the check to see if the object I want is in scope returns > true. But it still isn't available in Python. > > > I have tried scope.Eval(moduleName + ".Startup('" + pythonCommandLine + > "')") and Exec() with locals: > > Dim locals As PyDict = New PyDict() > locals.SetItem("Bridge".ToPython(), bridge.ToPython()) > scope.Exec(moduleName + ".Startup('" + pythonCommandLine + > "')", locals) > > All with the same effect. Any ideas on how to get set variables so > scripts can gain access to them? > > > Thanks, > > Steve > > > Please be advised that this email may contain confidential information. If > you are not the intended recipient, please notify us by email by replying > to the sender and delete this message. The sender disclaims that the > content of this email constitutes an offer to enter into, or the acceptance > of, any agreement; provided that the foregoing does not invalidate the > binding effect of any digital or other electronic reproduction of a manual > signature that is included in any attachment. > _________________________________________________ > Python.NET mailing list - PythonDotNet at python.org > https://mail.python.org/mailman/listinfo/pythondotnet > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Steve.Luke at moldev.com Mon Sep 24 09:59:30 2018 From: Steve.Luke at moldev.com (Luke, Steve) Date: Mon, 24 Sep 2018 13:59:30 +0000 Subject: [Python.NET] Using PyScope to set variables I can use in imported scripts In-Reply-To: References: , Message-ID: Yes, I did. All the unit tests for PyScope use the Eval(), Exec(), or Execute() functions to get code to execute in the PyScope. They don't use Import the way I had been (hoping to do). I am probably using it differently than intended/expected. I am importing a script into the scope and expect to be able to inject variables into the imported module. That doesn't seem to be what PyScope is used for. Instead, PyScope seems to be a container for the code and variables like the __main__ scope that gets created when running Python from the command prompt. It looks like when you Import it creates a new nested scope which doesn't have access to the scope created by PyScope. I had expected it to work as PyScope was the scope in which the Imported code would be executed, not the parent scope. I think the intended way to use PyScope and access the variables it holds is to pass the code in as Strings using Exec(). I think the right thing to do is: 1) Set variables in PyScope 2) Add the path to the script to the sys.path 3) Read the contents of the script file into a string 4) Use the PyScope to Exec() Execute() or Eval() as appropriate This works it is just not the same as what I was used to with IronPython (which is fine if that is really how it is intended to be used). Steve ________________________________ From: Denis Akhiyarov Sent: Monday, September 24, 2018 9:36:19 AM To: Luke, Steve; A list for users and developers of Python for .NET Cc: ywg Subject: Re: [Python.NET] Using PyScope to set variables I can use in imported scripts I copied Wenguang Yang (@yagweb), who developed PyScope. Steve, did you look at the unit tests for PyScope? Thanks, Denis On Mon, Sep 24, 2018 at 8:28 AM Luke, Steve > wrote: I am new to Python.Net and trying to convert an application that used IronPython to embed a Python environment into a .Net application and allow the user to run Python scripts inside our application. The part that embeds the Python environment is VB.Net (but could be C# if that would be better). I am running into problems setting variables in the scope such that they can be used in the Python scripts we call. I keep getting an error: NameError: global name 'Bridge' is not defined. This is the VB code I am trying to use: PythonEngine.Initialize() Using (Py.GIL()) Dim moduleName As String = "PrintToBridge" Dim pythonCommandLine As String = "Not used yet" Dim scope As PyScope = Py.CreateScope() Try scope.Set("Bridge", bridge) scope.Set("SetupParams", cmdLine) Dim script As Object = scope.Import(moduleName) bridge.mm.PrintMsg("Scope has Bridge:" + Str(scope.Contains("Bridge"))) script.Startup(pythonCommandLine) Finally scope.Dispose() End Try End Using The Python script: def Startup(param): Bridge.mm.PrintMsg('Startup called') def Docommand(param): Bridge.mm.PrintMsg('docommand called') def Shutdown(): Bridge.mm.PrintMsg('Shutdown called') How can I set a variable such that it would be recognized by the called Python script? Importing the script works, I can force python-y things to happen. And the check to see if the object I want is in scope returns true. But it still isn't available in Python. I have tried scope.Eval(moduleName + ".Startup('" + pythonCommandLine + "')") and Exec() with locals: Dim locals As PyDict = New PyDict() locals.SetItem("Bridge".ToPython(), bridge.ToPython()) scope.Exec(moduleName + ".Startup('" + pythonCommandLine + "')", locals) All with the same effect. Any ideas on how to get set variables so scripts can gain access to them? Thanks, Steve Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment. _________________________________________________ Python.NET mailing list - PythonDotNet at python.org https://mail.python.org/mailman/listinfo/pythondotnet Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment. -------------- next part -------------- An HTML attachment was scrubbed... URL: From mohamed.koubaa at ansys.com Mon Sep 24 10:03:52 2018 From: mohamed.koubaa at ansys.com (Mohamed Koubaa) Date: Mon, 24 Sep 2018 09:03:52 -0500 Subject: [Python.NET] Using PyScope to set variables I can use in imported scripts In-Reply-To: References: Message-ID: Steve, My organization is interested in migrating from IronPython to PythonNet so I'll add my two cents. We do this: 1. (during startup) Run a short python file which imports some .NET namespaces into a scope 2. (during startup) Add some variables to that scope (globals which give scripts access to our data model) 3. (at arbitrary times later) Run python code in the above scope so that there isn't a need to import the namespaces or discover the data model Thanks, Mohamed Koubaa ANSYS Inc On Mon, Sep 24, 2018 at 8:59 AM Luke, Steve wrote: > Yes, I did. All the unit tests for PyScope use the Eval(), Exec(), or > Execute() functions to get code to execute in the PyScope. They don't use > Import the way I had been (hoping to do). > > > I am probably using it differently than intended/expected. I am importing > a script into the scope and expect to be able to inject variables into the > imported module. That doesn't seem to be what PyScope is used for. > Instead, PyScope seems to be a container for the code and variables like > the __main__ scope that gets created when running Python from the command > prompt. It looks like when you Import it creates a new nested scope which > doesn't have access to the scope created by PyScope. I had expected it > to work as PyScope was the scope in which the Imported code would be > executed, not the parent scope. I think the intended way to use PyScope > and access the variables it holds is to pass the code in as Strings using > Exec(). > > > I think the right thing to do is: > > 1) Set variables in PyScope > 2) Add the path to the script to the sys.path > > 3) Read the contents of the script file into a string > > 4) Use the PyScope to Exec() Execute() or Eval() as appropriate > > > This works it is just not the same as what I was used to with IronPython > (which is fine if that is really how it is intended to be used). > > > Steve > ------------------------------ > *From:* Denis Akhiyarov > *Sent:* Monday, September 24, 2018 9:36:19 AM > *To:* Luke, Steve; A list for users and developers of Python for .NET > *Cc:* ywg > *Subject:* Re: [Python.NET] Using PyScope to set variables I can use in > imported scripts > > I copied Wenguang Yang (@yagweb), who developed PyScope. > > Steve, did you look at the unit tests for PyScope? > > Thanks, > Denis > > On Mon, Sep 24, 2018 at 8:28 AM Luke, Steve wrote: > > I am new to Python.Net and trying to convert an application that used > IronPython to embed a Python environment into a .Net application and allow > the user to run Python scripts inside our application. The part that > embeds the Python environment is VB.Net (but could be C# if that would be > better). > > > I am running into problems setting variables in the scope such that they > can be used in the Python scripts we call. I keep getting an error: > NameError: global name 'Bridge' is not defined. > > > This is the VB code I am trying to use: > > PythonEngine.Initialize() > > Using (Py.GIL()) > Dim moduleName As String = "PrintToBridge" > Dim pythonCommandLine As String = "Not used yet" > > Dim scope As PyScope = Py.CreateScope() > Try > scope.Set("Bridge", bridge) > scope.Set("SetupParams", cmdLine) > > Dim script As Object = scope.Import(moduleName) > bridge.mm.PrintMsg("Scope has Bridge:" + > Str(scope.Contains("Bridge"))) > script.Startup(pythonCommandLine) > Finally > scope.Dispose() > End Try > End Using > > The Python script: > > def Startup(param): > Bridge.mm.PrintMsg('Startup called') > > def Docommand(param): > Bridge.mm.PrintMsg('docommand called') > > def Shutdown(): > Bridge.mm.PrintMsg('Shutdown called') > > > How can I set a variable such that it would be recognized by the called > Python script? Importing the script works, I can force python-y things to > happen. And the check to see if the object I want is in scope returns > true. But it still isn't available in Python. > > > I have tried scope.Eval(moduleName + ".Startup('" + pythonCommandLine + > "')") and Exec() with locals: > > Dim locals As PyDict = New PyDict() > locals.SetItem("Bridge".ToPython(), bridge.ToPython()) > scope.Exec(moduleName + ".Startup('" + pythonCommandLine + > "')", locals) > > All with the same effect. Any ideas on how to get set variables so > scripts can gain access to them? > > > Thanks, > > Steve > > > Please be advised that this email may contain confidential information. If > you are not the intended recipient, please notify us by email by replying > to the sender and delete this message. The sender disclaims that the > content of this email constitutes an offer to enter into, or the acceptance > of, any agreement; provided that the foregoing does not invalidate the > binding effect of any digital or other electronic reproduction of a manual > signature that is included in any attachment. > _________________________________________________ > Python.NET mailing list - PythonDotNet at python.org > https://mail.python.org/mailman/listinfo/pythondotnet > > > Please be advised that this email may contain confidential information. If > you are not the intended recipient, please notify us by email by replying > to the sender and delete this message. The sender disclaims that the > content of this email constitutes an offer to enter into, or the acceptance > of, any agreement; provided that the foregoing does not invalidate the > binding effect of any digital or other electronic reproduction of a manual > signature that is included in any attachment. > _________________________________________________ > Python.NET mailing list - PythonDotNet at python.org > https://mail.python.org/mailman/listinfo/pythondotnet > -------------- next part -------------- An HTML attachment was scrubbed... URL: From denis.akhiyarov at gmail.com Mon Sep 24 10:20:33 2018 From: denis.akhiyarov at gmail.com (Denis Akhiyarov) Date: Mon, 24 Sep 2018 09:20:33 -0500 Subject: [Python.NET] Invoking a C# method with a python list In-Reply-To: References: Message-ID: This link below is probably a good staring point for "TypeError: No method matches given arguments". But ideally we should bring back the skipping of method resolution logic within a context manager whenever the user knows what he/she is doing. There is also explicit `.Overloads` option to force through one of the overloaded methods. https://github.com/pythonnet/pythonnet/issues/265 On Thu, Aug 2, 2018 at 10:11 AM Mohamed Koubaa wrote: > Hello, > > I am using the 2.3 release branch. > > I have C# code like this: > public class A { > public A() {...} > } > > public class B { > public B() {...} > public static void Func(IEnumerable input){ > } > } > > I want to pass in a python list to Func but get this error: > >>> b=B() > >>> b.Func([A(), A()]) > Traceback (most recent call last): > File "", line 1, in > TypeError: No method matches given arguments > > I tried both IEnumerable and IList in the signature of 'Func'. I know > that I can import System and create some .NET collection type and it works > well. > > Did I miss anything? Is this usage pattern within the design goals of > pythonnet? I assume this would have to be fixed in MethodBinder but I > wanted to try asking here first. > > Thanks! > Mohamed Koubaa > _________________________________________________ > Python.NET mailing list - PythonDotNet at python.org > https://mail.python.org/mailman/listinfo/pythondotnet > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mohamed.koubaa at ansys.com Mon Sep 24 11:11:02 2018 From: mohamed.koubaa at ansys.com (Mohamed Koubaa) Date: Mon, 24 Sep 2018 10:11:02 -0500 Subject: [Python.NET] Invoking a C# method with a python list In-Reply-To: References: Message-ID: Denis, I like the idea of empowering the user to customize/extend/disable the method resolution logic. I find that IronPython's resolution isn't always ideal for our use cases. For example we have methods which take IEnumerable and this usage is currently rejected by IronPython: Obj.Func([1,3,4]) while this works Obj.Func([1.,3.,4.]) I am not saying that IronPython is wrong but that as an API designer I'd like the option to choose to allow the former for all of my IEnumerable methods or even to customize the resolution at the granularity of individual methods/classes where we decide that is the appropriate "User Experience". I see a unit test on master with the .Overloads method being called from python but this is useful for me only as a workaround. We aim to present a clean API to our scripting users who often are not experienced programmers. Thanks, Mohamed Koubaa ANSYS Inc On Mon, Sep 24, 2018 at 9:20 AM Denis Akhiyarov wrote: > This link below is probably a good staring point for "TypeError: No > method matches given arguments". But ideally we should bring back the > skipping of method resolution logic within a context manager whenever the > user knows what he/she is doing. There is also explicit `.Overloads` option > to force through one of the overloaded methods. > > https://github.com/pythonnet/pythonnet/issues/265 > > On Thu, Aug 2, 2018 at 10:11 AM Mohamed Koubaa > wrote: > >> Hello, >> >> I am using the 2.3 release branch. >> >> I have C# code like this: >> public class A { >> public A() {...} >> } >> >> public class B { >> public B() {...} >> public static void Func(IEnumerable input){ >> } >> } >> >> I want to pass in a python list to Func but get this error: >> >>> b=B() >> >>> b.Func([A(), A()]) >> Traceback (most recent call last): >> File "", line 1, in >> TypeError: No method matches given arguments >> >> I tried both IEnumerable and IList in the signature of 'Func'. I know >> that I can import System and create some .NET collection type and it works >> well. >> >> Did I miss anything? Is this usage pattern within the design goals of >> pythonnet? I assume this would have to be fixed in MethodBinder but I >> wanted to try asking here first. >> >> Thanks! >> Mohamed Koubaa >> _________________________________________________ >> Python.NET mailing list - PythonDotNet at python.org >> https://mail.python.org/mailman/listinfo/pythondotnet >> > _________________________________________________ > Python.NET mailing list - PythonDotNet at python.org > https://mail.python.org/mailman/listinfo/pythondotnet > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tunger at mitem.com Mon Sep 24 12:02:30 2018 From: tunger at mitem.com (Tom Unger) Date: Mon, 24 Sep 2018 16:02:30 +0000 Subject: [Python.NET] Using PyScope to set variables I can use in imported scripts In-Reply-To: References: , Message-ID: I too am working (slowly) on adding pythonnet as scripting environment for a .NET application. When investigating this I wrote a VS project that tested all the assumptions I was making about how the interface worked. That is available on GitHub at: https://github.com/tomunger/pythonnet.embedingtest Below is the method I use to load a module. Note that I don?t import the module but execute it in the PyScope. Tom private PyScope LoadModule(string name, string fileName, Dictionary globals) { PyScope scope = null; using (Py.GIL()) { // Create a new scope scope = Py.CreateScope(name); // Assign any globals. if (globals != null) { foreach (string gname in globals.Keys) scope.Set(gname, globals[gname]); } // Load python code, compile, then execute it in the scope. string scriptText = File.ReadAllText(fileName); PyObject code = PythonEngine.Compile(scriptText, fileName); dynamic r = scope.Execute(code); } return scope; } ________________________________ From: PythonDotNet on behalf of Mohamed Koubaa Sent: Monday, September 24, 2018 7:03:52 AM To: pythondotnet at python.org Cc: hbtmdxywg at 126.com Subject: Re: [Python.NET] Using PyScope to set variables I can use in imported scripts Steve, My organization is interested in migrating from IronPython to PythonNet so I'll add my two cents. We do this: 1. (during startup) Run a short python file which imports some .NET namespaces into a scope 2. (during startup) Add some variables to that scope (globals which give scripts access to our data model) 3. (at arbitrary times later) Run python code in the above scope so that there isn't a need to import the namespaces or discover the data model Thanks, Mohamed Koubaa ANSYS Inc On Mon, Sep 24, 2018 at 8:59 AM Luke, Steve > wrote: Yes, I did. All the unit tests for PyScope use the Eval(), Exec(), or Execute() functions to get code to execute in the PyScope. They don't use Import the way I had been (hoping to do). I am probably using it differently than intended/expected. I am importing a script into the scope and expect to be able to inject variables into the imported module. That doesn't seem to be what PyScope is used for. Instead, PyScope seems to be a container for the code and variables like the __main__ scope that gets created when running Python from the command prompt. It looks like when you Import it creates a new nested scope which doesn't have access to the scope created by PyScope. I had expected it to work as PyScope was the scope in which the Imported code would be executed, not the parent scope. I think the intended way to use PyScope and access the variables it holds is to pass the code in as Strings using Exec(). I think the right thing to do is: 1) Set variables in PyScope 2) Add the path to the script to the sys.path 3) Read the contents of the script file into a string 4) Use the PyScope to Exec() Execute() or Eval() as appropriate This works it is just not the same as what I was used to with IronPython (which is fine if that is really how it is intended to be used). Steve ________________________________ From: Denis Akhiyarov > Sent: Monday, September 24, 2018 9:36:19 AM To: Luke, Steve; A list for users and developers of Python for .NET Cc: ywg Subject: Re: [Python.NET] Using PyScope to set variables I can use in imported scripts I copied Wenguang Yang (@yagweb), who developed PyScope. Steve, did you look at the unit tests for PyScope? Thanks, Denis On Mon, Sep 24, 2018 at 8:28 AM Luke, Steve > wrote: I am new to Python.Net and trying to convert an application that used IronPython to embed a Python environment into a .Net application and allow the user to run Python scripts inside our application. The part that embeds the Python environment is VB.Net (but could be C# if that would be better). I am running into problems setting variables in the scope such that they can be used in the Python scripts we call. I keep getting an error: NameError: global name 'Bridge' is not defined. This is the VB code I am trying to use: PythonEngine.Initialize() Using (Py.GIL()) Dim moduleName As String = "PrintToBridge" Dim pythonCommandLine As String = "Not used yet" Dim scope As PyScope = Py.CreateScope() Try scope.Set("Bridge", bridge) scope.Set("SetupParams", cmdLine) Dim script As Object = scope.Import(moduleName) bridge.mm.PrintMsg("Scope has Bridge:" + Str(scope.Contains("Bridge"))) script.Startup(pythonCommandLine) Finally scope.Dispose() End Try End Using The Python script: def Startup(param): Bridge.mm.PrintMsg('Startup called') def Docommand(param): Bridge.mm.PrintMsg('docommand called') def Shutdown(): Bridge.mm.PrintMsg('Shutdown called') How can I set a variable such that it would be recognized by the called Python script? Importing the script works, I can force python-y things to happen. And the check to see if the object I want is in scope returns true. But it still isn't available in Python. I have tried scope.Eval(moduleName + ".Startup('" + pythonCommandLine + "')") and Exec() with locals: Dim locals As PyDict = New PyDict() locals.SetItem("Bridge".ToPython(), bridge.ToPython()) scope.Exec(moduleName + ".Startup('" + pythonCommandLine + "')", locals) All with the same effect. Any ideas on how to get set variables so scripts can gain access to them? Thanks, Steve Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment. _________________________________________________ Python.NET mailing list - PythonDotNet at python.org https://mail.python.org/mailman/listinfo/pythondotnet Please be advised that this email may contain confidential information. If you are not the intended recipient, please notify us by email by replying to the sender and delete this message. The sender disclaims that the content of this email constitutes an offer to enter into, or the acceptance of, any agreement; provided that the foregoing does not invalidate the binding effect of any digital or other electronic reproduction of a manual signature that is included in any attachment. _________________________________________________ Python.NET mailing list - PythonDotNet at python.org https://mail.python.org/mailman/listinfo/pythondotnet -------------- next part -------------- An HTML attachment was scrubbed... URL: