Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Support both Object and Variant return types in UDFs. #2312

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

rudolfbyker
Copy link

@rudolfbyker rudolfbyker commented Aug 10, 2023

I tested this manually in our system, and it seems to work.

Sorry, no unit tests, because I could not figure out how to get tests running in this project.

@fzumstein
Copy link
Member

I added instructions on how to run the UDF tests: https://github.com/xlwings/xlwings/blob/main/DEVELOPER_GUIDE.md#tests

If no unit test, a code sample of what this PR allows you to do would be great.

@fzumstein
Copy link
Member

And ignore the pipeline failure, that's now fixed on main.

@rudolfbyker
Copy link
Author

rudolfbyker commented Aug 22, 2023

OK, so I ran into a few problems here... Please bear with me.

I added instructions on how to run the UDF tests: https://github.com/xlwings/xlwings/blob/main/DEVELOPER_GUIDE.md#tests

The only relevant thing I see here is:

To run the UDF tests, open udf_tests.xlsm in the tests/udfs directory and follow the instructions at the top of the file. The other files work accordingly.

I created a venv for xlwings development. If I don't install anything, and just follow these instructions, I get ModuleNotFoundError: No module named 'nose'. Then I went looking for requirements.txt, setup.py or pyproject.toml files. None of them contain nose. When I run pip install nose, I get Requirement already satisfied: nose.

Then I noticed that xlwings is using the wrong Python. To try to fix that, I ran xlwings addin install from within the venv, but that gave me a dependency error.

To try to install dependencies, I ran pip install . inside the repo folder, using the venv, but that told me I need Rust installed...

It looks like I'm going to have to build a separate VM for xlwings development, and install a lot of dependencies. I would really like to help you here, but without better instructions on how to get started, that's almost impossible.


If no unit test, a code sample of what this PR allows you to do would be great.

EDIT: I provided a bogus example here. New example is at #2312 (comment) below.

Use cases:

  • Creating VBA objects in VBA, keeping them in memory in Python, and passing them back as arguments to other VBA functions when calling them from Python.
  • Python UDFs that sometimes returns a VBA object, and sometimes returns a scalar value.

@fzumstein
Copy link
Member

fzumstein commented Aug 23, 2023

Thanks for the sample and fair point about nose! I removed that dependency now on the main branch.

@fzumstein
Copy link
Member

How to get by without Rust is explained here: https://github.com/xlwings/xlwings/blob/main/DEVELOPER_GUIDE.md#python-package

@rudolfbyker
Copy link
Author

rudolfbyker commented Oct 5, 2023

I'm on a fresh Windows 10 installation, I followed the steps at https://github.com/xlwings/xlwings/blob/main/DEVELOPER_GUIDE.md#tests exactly (even with Rust), and I had to do two one extra thing which are not specified in the developer guide to get it to work:

  • Specify my interpreter path in the "Interpreter" field on the xlwings ribbon, since I'm using a venv.
  • pip install nose Sorry, I wasn't on the latest main branch :)

Also, the correct place for the dlls seems to be directly in the venv folder, rather than next to python.exe (which is in venv/Scripts).

But the good news is that I can run the UDF tests now.

@fzumstein
Copy link
Member

@rudolfbyker
Copy link
Author

rudolfbyker commented Oct 5, 2023

As I'm writing the unit tests, I realized that I gave a bogus example above of what I'm trying to do! So let me clarify the use case:

1. Returning arbitrary objects from VBA to Python already works

Public Function foo() As Collection
    Set foo = New Collection
    foo.Add "bar"
    foo.Add "baz"
End Function
vba_collection_object = wb.macro("foo")()

2. Returning objects like a dict from Python to VBA already works

  • This is already covered by the write_dict UDF test.
  • It does not do what I want it to do, so I never use this.
  • Is there already a unit test for this somewhere? I could not find it. I can add one if you like.

3. Returning arbitrary VBA objects from Python to VBA (not from VBA to Python)

This is what is currently not possible, and what my PR will solve.

@xw.func
def return_vba_dict() -> Any:
    return create_vba_dict({"a": 1, "b": 2})


def create_vba_dict(d: Mapping[str, Any]) -> Any:
    """
    Create a VBA dictionary.

    See https://stackoverflow.com/questions/67397267/pass-dictionary-to-excel-macro-using-win32com-and-comtypes
    """
    import win32com.client

    result = win32com.client.Dispatch("Scripting.Dictionary")

    for key, value in d.items():
        result[key] = value

    return result

Similarly, we might want to pass something returned in case 1 above (e.g. the collection object or some other object) back to VBA at some point.

Without my PR, we will get an error like this:

image

With my PR and the example above, it works, and VBA gets a bona fide Dictionary object back from Python.

@rudolfbyker
Copy link
Author

Rebased and added tests :)

@fzumstein
Copy link
Member

Awesome, thanks!

@rudolfbyker
Copy link
Author

Rebased again. Please consider.

@fzumstein
Copy link
Member

thanks, I'll try to get to it next week

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants