Q: Python is it OK to import the same library in a def? (read: multiple times)

Hi,

Keep imports on top of your file. There is (almost) no reason to do an import within a function.
If you want to test single functions, keep them in your module, import your module and execute the function from your library inside the script editor. Instead of testing each function manually, also think about unittests. Separate GUI interaction (=ViewModel) from Core (=Model) logic. Getting geometry and input arguments and outputting them, has nothin to do with creating, modifying or validating geometry. It shouldn’t matter if you call it from a Rhino Command or a Grasshopper Component (ideally).

1 Like

Oh… thanks! But that was a lot of terms that I am unfamiliar with :slight_smile:
What I am working on is Holomark3 where I want to run 6 CPU tests and 6 GPU tests + render tests and then the score handling and system data gathering.

I keep them separate when I program them to keep everything easy to maintain for my brain. So I put the data in a definition and call that. Then I put all those definitions in one final script at the end and compile that to a plugin.

Do you think I should do it differently? It’s important for me to do this as simple as possible so I can work on it every now and then without getting lost in my own complexity :wink:

I don’t get the second step. Is the command-compiler not able to deal with modules?

Usually you split a larger piece of code into smaller files until it fullfills the ‘Single Responsibilty’ rule.
If you programm object-oriented you put 1 class into 1 file. And this one class usually does only one major thing. But even without classes, the idea is to separate concerns.
Each file has its own dependencies. Of course reducing the dependencies is a good thing. Dependency inversion/injection. Your module/component/class shouldn’t rely on too many external code. Instead you design it to deal with abstractions instead of concrete implementations.
If you encapsulate well, (unit) testing is simple. Sounds complicated but is actually really easy. And this helps dealing with many lines of code.

Example: You don’t need to run 6 cpu and 6 gpu tests to see if the report is written correctly. If you encapsulate the reporting into a Report class (or reporting module), you can test that totally isolated. Of course the arguments for writing the report have to be abstract enough to be mocked.

Hello,

It is definitely good practice to put all your imports at the top of the file, starting with standard library imports like os, csv, sys and following on with things like Rhino and your own submodules and to organise your code in functions in separate .py files and import the functions you need into your main file

from Holomark3 import CPUTest1, CPUTest2, ...


def main():
    CPUTest1()
    CPUTest2()
    

if __name__ == '__main__':
    main()

The main gotchas to look out for are any code which is executed during import (should be in the __init.py file) and overwriting names, for instance if you import Rhino and also Rhino.Geometry as rg etc then depending on the order of import, later import statements may not work properly. Overall you should be fine.

Also you can use a tool like isort to sort your imports - you will need a separate Cpython installed on your system but it should work fine

Nice, that was visually easy to understand.
So can I just import any .py file that is located at the same location as the running .py file?
And if so do I just put those files together in the compiler, or do I just put the ‘mother’ .py file there and the compiler reads everything it needs from it??

Sorry for being a total n00b at this, I have evolved from a macro user to a simple scripter who does way to much with way to little coding knowledge :wink:

No worries I’m on the same journey really :snake: but probably with much more deliberate python learning thrown in (books, podcasts, online courses, knowledge sharing here…) :slight_smile:

Yes you can put all the python files next to one another, however I’m not sure about packaging them with the compiler - there was some discussion about this over the last many years on this thread which suggests that it is possible to put multiple files side-by-side and includes a video and a plan to write some documentation…

Thanks again, I’ll do some tests!

It is also possible to add other files to the zip (The installer is just a renamed zip file) and then all of those are installed to the same location. I do that with display modes and rhino files that are used for content and templates for Holomark2 (and will for HM3 too of course)

1 Like

I do recommend coding this way in general where you break up your logic into easy to understand libraries of files. Unfortunately, the script compiler is not currently set up to be able to include packages. It’s been on my todo list for a long time.

3 Likes

Hello Steve,

Does that mean that it is ok to break the logic into multiple adjacent files but a package (a subfolder with an __init.py file inside) will not work?

Scripts are currently embedded as strings inside of commands. This means there is no local working directory where the script is executing as there is no python file.

OK, so spoon-feeding my self here:
I need to put all the parts into one .py file before making a plugin (like I have always done), right?

That is correct.

1 Like

I wonder whether the builtin zipimport module would help with this …? 31.4. zipimport — Import modules from Zip archives — Python 2.7.18 documentation

I don’t understand what you are recommending. Can you elaborate?

Ahh I might be a bit out of date - because rhino packages were zip files I thought the ability to directly import from a zip file might help but that may not be the case for yak? I also had vague ideas around using the built-in python tokeniser and getsource modules to flatten a package into one string or file but couldn’t find an existing robust looking open source example. I have played with these a little to correctly handle the import statements and docstrings in my rhinoscriptsyntax getsource utility. …

Hi Holo,

I have been discussing this with my local Python user group - what path do you use to find your image assets from python? (sorry this is surely in the docs somewhere)

If you start your script with

import sys
path_to_assets = r'path\to\assets' # not literally, obvs
sys.path.insert(0, path_to_assets)

from Holomark3 import CPUTest1, CPUTest2, ...

Does that work ? The idea is to add the correct path so Python can find the other files…

-Graham

This is a way to package modularized scripts in an RHI:
Creating Rhino Commands Using Python - Creating an RHI Installer

Unfortunately, as has been reported

when updates are installed, the package with the lowest numerical version in __plugin__.py is used by Rhino.

1 Like

I wouldn’t recommend going this route. Using the script compiler and yak is going to be better supported in the long run.

So is there a path to image/zip/.py files packaged via the package manager which would allow the Ironpython interpreter to find those files during execution ? I guess another option for me distributing toolbars with associated scripts at work is to copy the associated python files / modules from the network on first execution…

Technically, you should be able to package for support scripts today. It just isn’t nearly as straightforward as it should be.

A yak package is a zip file. If you place a directory of support scripts (a python package) inside of this yak package, it will be distributed to users. The yak package is unzipped to a special versioned location on an end user’s computer.

The script compiler makes Rhino commands that execute python scripts. These commands belong to a PlugIn class. You can get a path to this PlugIn by calling PathFromId or PathFromName. Your directory of supporting python scripts will be in this location which you can then use to update your python path in your script.

https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_PlugIns_PlugIn_PathFromId.htm
https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_PlugIns_PlugIn_PathFromName.htm

2 Likes