So, moving on from hello world programs, next I want to get Python to factorize quadratic expressions to show off what one can do in Python. Sympy does symbolic manipulation of math expressions. So I want to import from sympy.
Bad Indent reveals error stack and policy.py
So my first attempt, I pass an extra parameter into Greeting because I will want to pass equations. That worked. So now I want to import from sympy.
class QuadEqFactorizer(object):
_reg_clsid_ = "{CAAA6723-6A89-4BCD-A375-43BF4C06B01B}"
_reg_progid_= 'SciPyInVBA.QuadEqFactorizer'
_public_methods_ = ['Greeting']
def Greeting(self,name):
import sympy as sp #accidentally got indentation wrong
return ("Hello world " + name)
if __name__=='__main__':
print ("Registering COM server...")
import win32com.server.register
win32com.server.register.UseCommandLine(QuadEqFactorizer)
And the VBA looks like this
Sub Test()
Dim obj As Object
Set obj = CreateObject("SciPyInVBA.QuadEqfactorizer")
Debug.Print obj.greeting("Dave")
End Sub
And in the VBA on the CreateObject line I get the following error
Unexpected Python Error: Traceback (most recent call last):
File "C:\Progra~2\Micros~4\Shared\Python~1\lib\site-packages\win32com\server\policy.py", line 136, in CreateInstance
return retObj._CreateInstance_(clsid, reqIID)
File "C:\Progra~2\Micros~4\Shared\Python~1\lib\site-packages\win32com\server\policy.py", line 194, in _CreateInstance_
myob = call_func(classSpec)
File "C:\Progra~2\Micros~4\Shared\Python~1\lib\site-packages\win32com\server\policy.py", line 728, in call_func
return resolve_func(spec)(*args)
File "C:\Progra~2\Micros~4\Shared\Python~1\lib\site-packages\win32com\server\policy.py", line 717, in resolve_func
module = _import_module(mname)
File "C:\Progra~2\Micros~4\Shared\Python~1\lib\site-packages\win32com\server\policy.py", line 736, in _import_module
__import__(mname)
File "C:\Users\Simon\source\repos\QuadEqFactorizer\QuadEqFactorizer\QuadEqFactorizer.py", line 7
import sympy as sp
^
IndentationError: expected an indented block
So this simple indention error revealed a whole error stack, the source for policy.py looks interesting but I'll leave that for another day.
Indentation matters in Python, it defines code blocks without resorting to curly brackets (like C++,C#,Java,Javascript) or If..End If blocks like VBA. Note the trailing colons on some lines signifying start of a code block.
Installing sympy
Even after fixing indentation I got the error No module named 'sympy'. So I open command window and ran pip install sympy but got access error. Tried again with a new command window with admin rights and it worked.
C:\WINDOWS\system32>pip install sympy
Collecting sympy
Using cached https://files.pythonhosted.org/packages/91/26/4e477dbd1f9260eb743d9f221af3044648a8fb2fcf3f2f23daee4dc831a4/sympy-1.1.1.tar.gz
Collecting mpmath>=0.19 (from sympy)
Using cached https://files.pythonhosted.org/packages/7a/05/b3d1472885d8dc0606936ea5da0ccb1b4785682e78ab15e34ada24aea8d5/mpmath-1.0.0.tar.gz
Installing collected packages: mpmath, sympy
Running setup.py install for mpmath ... done
Running setup.py install for sympy ... done
Successfully installed mpmath-1.0.0 sympy-1.1.1
And now the code works but still returns Hello World. Let's move on and start calling sympy's factor().
sympy's factor return value cannot be converted to a COM_VARIANT
So we can call sympy's factor function to simplify quadratic equations but its return value is not returnable via COM and it cannot be converted to a COM_VARIANT.
def Factorize(self,sEquation):
import sympy as sp
return sp.factor (sEquation)
this is the exact error message
Unexpected Python Error: TypeError: Objects of type 'Pow' can not be converted to a COM VARIANT (but obtaining the buffer() of this object could).
Wrap result in str() got it working
I'm guessing 'Pow' referred to the exponent term in the result and that it is complaining about my attempts to return the object to VBA without converting it to a string. It turns out that all we need is to wrap the answer in str(). [N.B. buffer() was a misleading error.] The final code listings are
class QuadEqFactorizer(object):
_reg_clsid_ = "{CAAA6723-6A89-4BCD-A375-43BF4C06B01B}"
_reg_progid_= 'SciPyInVBA.QuadEqFactorizer'
_public_methods_ = ['Factorize']
def Factorize(self,sEquation):
import sympy as sp
factorized = sp.factor (sEquation)
return str(factorized)
if __name__=='__main__':
print ("Registering COM server...")
import win32com.server.register
win32com.server.register.UseCommandLine(QuadEqFactorizer)
And the VBA is thus
Sub Test()
Dim obj As Object
Set obj = CreateObject("SciPyInVBA.QuadEqfactorizer")
Debug.Print obj.Factorize("x**2 + 2*x + 1")
Debug.Print obj.Factorize("x**2 - x - 6")
End Sub
Which when run produces the output
(x + 1)**2
(x - 3)*(x + 2)
Final Thoughts
I do not believe a VBA programmer has any other open source options if they want to symbolically manipulate mathematical expressions. This is marvellous. One can now do one's math homework in VBA (whilst enlisting Python!)
No comments:
Post a Comment