CPython in Grasshopper - Not loading "pywin32" issues with xlwings

I am using python in grasshopper and at the end of the model script I am trying to send data to excel using xlwings. The issue I’m running into is that xlwings can’t load pywin32. Everything is installed just not communicating with each other.

Imports at the top of the script:

xlwings error:

the text of the error:

the error that’s running is:

Traceback (most recent call last):
File “rhinocode:///grasshopper/1/7cf354f0-5afa-4c40-b8f8-873feaae7da1/5fda766b-8a2e-4e25-b1a0-d2d4be595dba”, line 365, in
File “C:\Users\smcauliffe.rhinocode\py39-rh8\site-envs\default-Ohtn8jFK\xlwings\main.py”, line 959, in init
if apps.active:
File “C:\Users\smcauliffe.rhinocode\py39-rh8\site-envs\default-Ohtn8jFK\xlwings\main.py”, line 220, in active
for app in self.impl:
File “C:\Users\smcauliffe.rhinocode\py39-rh8\site-envs\default-Ohtn8jFK\xlwings\main.py”, line 5276, in impl
raise XlwingsError(
XlwingsError: Make sure to have “pywin32”, a dependency of xlwings, installed.

Is there an example script you can share that I can test and replicate the issue here?

@eirannejad
Sorry I never followed up but I figured it out. I skimmed over a previous post and didn’t see the nomenclature “#r: xlwings, pywin32

I was specifying separately:
#r: xlwings
#r: pywin32

and Grasshopper didn’t like that.

Thanks!!

2 Likes

Sweet. Glad it is working :smiley:

I’m glad too. Have you got any idea why the CPython component’s pip or library installer didn’t pick up the transitive dependency? It looks declared correctly to me.

Just now, when only asked to install xlwings, uv installed pywin32 just now in Python 3.12 and 3.9 (it down graded all the versions of xlwings to 0.11.7 in Python 3.14, though I think that’s just too new).


dependencies = [
    "pywin32 >= 224;platform_system=='Windows'",
1 Like

No idea to be honest. I gonna look deeper to see where the problem is, but the package spec in #r: xlwings is passed directly to pip install command so it seems like pip can not resolve the dependency :thinking: It might be because we have pywin32 installed in a different environment. Maybe.

1 Like

Good news. It does try to install pywin32 as transitive dep. But I couldn’t import pywin32 afterwards. Nor before installing xlwings either.

Looking in %USERPROFILE%\.rhinocode\py39-rh8\site-envs\default-gA2T3VSp:
I only see folders named pywin32_system32, pywin32-308.dist-info, and:

pywin32.pth

# .pth file for the PyWin32 extensions
win32
win32\lib
Pythonwin
# And some hackery to deal with environments where the post_install script
# isn't run.
import pywin32_bootstrap

Does the CPython component treat .pth files the same as a regular CPython run time?

I was able to import pywin32_bootstrap, and its __file__ is: .rhinocode\py39-rh8\site-support\win32\lib\pywin32_bootstrap.py . That still didn’t enable pywin32 to be imported, though there is another copy of those pywin32 folders (of a different version) in site-support.

I had a bunch of other libraries previously installed in that env too, so this wasn’t a clean test.

1 Like

I just ran this script in Rhino 8.17 SRC and it worked. @James_Parrott Does this not work for you? The installer handles .pth files and adds the paths to the search paths. If you see errors what does RhinoCodeLogs show? Would you mind sharing that as well?

Great. import xlwings did work with the extra #r: pywin32, but not without it, which is what I was curious about. Maybe it’s just another one that needs to be installed carefully like pandas (or is that fixed now?). I’ll upgrade to 8.17 too and try it again though.

1 Like

Ya probably. I also pushed a few more fixes for this that will be in 8.18 and I will test there as well.

1 Like

Good news and bad news. I deleted the normal env of installed directories, which usually forces Rhino to recreate it. Updated to 8.17 SRC. In a CPython3 component, both import xlwings and import pywin32 (with no #r:s) both raised module not found errors. Then this worked like a charm:

#r: xlwings 

import xlwings

print(xlwings)
print(f'{xlwings.__version__=}')

RhinoCodeLogs

Info 02/27/2025 11:44:37 [RhinoCode] Installing "xlwings"
Info 02/27/2025 11:44:37 [RhinoCode] Running process: \.rhinocode\py39-rh8\python.exe -I -m pip --isolated --disable-pip-version-check install --target "\.rhinocode\py39-rh8\site-envs\default-CsO7suzN" --progress-bar off --upgrade --no-warn-script-location --retries 5 --timeout 15 "xlwings"
Info 02/27/2025 11:46:09 [RhinoCode] Running process: \.rhinocode\py39-rh8\python.exe -I -m pip --isolated --disable-pip-version-check list --path "\.rhinocode\py39-rh8\site-envs\default-CsO7suzN" --format freeze
Info 02/27/2025 11:46:12 [RhinoCode] Collecting xlwings
  Using cached xlwings-0.33.9-cp39-cp39-win_amd64.whl.metadata (5.8 kB)
Collecting pywin32>=224 (from xlwings)
  Using cached pywin32-308-cp39-cp39-win_amd64.whl.metadata (8.3 kB)
Using cached xlwings-0.33.9-cp39-cp39-win_amd64.whl (1.6 MB)
Using cached pywin32-308-cp39-cp39-win_amd64.whl (6.6 MB)
Installing collected packages: pywin32, xlwings
Successfully installed pywin32-308 xlwings-0.33.9
<module 'xlwings' from ' ... \\.rhinocode\\py39-rh8\\site-envs\\default-CsO7suzN\\xlwings\\__init__.py'>
xlwings.__version__='0.33.9'

However this doesn’t work:

#r: xlwings 

import xlwings, pywin32

def print_mod_and_ver(module):
    print(module)
    print(f'{module.__version__=}')

print_mod_and_ver(xlwings)
print_mod_and_ver(pywin32)

xlwings masks pywin32 import errors. Maybe it has other useful features that don’t need it:

__init__.py

# Populate engines list
has_pywin32 = False
if sys.platform.startswith("win"):
    try:
        from . import _xlwindows

        engines.add(Engine(impl=_xlwindows.engine))
        has_pywin32 = True
    except ImportError:
        pass

_xlwings.py

import pywintypes
import win32api
import win32con

It takes a whole bunch of extra setup steps in that too.

After that, adding pywin32 as in your example:

#r: xlwings 
#r: pywin32

import xlwings
import win32api

produces:

Traceback (most recent call last):
  File "rhinocode:///grasshopper/1/35897f9d-b3b4-43ea-a00f-158e522fc36a/e251201f-92fc-4341-a897-d86b4f7c78af", line 5, in <module>
ModuleNotFoundError: No module named 'win32api'

It could be anything then. But as well as the Import Error mask, xlwings is doing a bunch of non-standard stuff xlwings/xlwings/_xlwindows.py at 8137da169dd1e5550ac65e302609c07468743755 · xlwings/xlwings · GitHub so maybe it picks up the pywin32 that’s already in Rhino’s CPython3?

I’m happy to believe that xlwings does its imports the way it does for a good reason. But at the end of the day, there’s not much more to a .xlsx file than a zipped xml file. So if the xlwings team aren’t going to support running in Rhino’s Pythons and others, (even for customers on their $1490 a year professional plan) I’d recommend people choose an alternative library without these issues.

I’ve had good results using: Welcome to pylightxl documentation — pylightxl 2019 documentation and made a fork for Iron Python (that only changes one character, to use a different core xml lib). I found the advice in this site to be useful: https://www.python-excel.org/