Is it possible to pass CLI arguments to a FreeCADCMD script?

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
Coreform_Greg
Posts: 2
Joined: Tue Oct 12, 2021 11:25 pm

Is it possible to pass CLI arguments to a FreeCADCMD script?

Post by Coreform_Greg »

Following guidance from https://wiki.freecad.org/Embedding_FreeCAD, I have a "master" Python script that needs to run FreeCAD as a subroutine. I'm unable to import FreeCAD likely due to the caveat regarding compilers:
Since the FreeCAD Python module is compiled from C++ (rather than being a pure Python module), it can only be imported from a compatible Python interpreter. Generally this means that the Python interpreter must be compiled with the same C compiler as was used to build FreeCAD. Information about the compiler used to build a Python interpreter (including the one built with FreeCAD) can be found as follows
Following the advice on the page, I'm instead going to issue a Python subprocess.run( cli_command, shell=True, check=True) from my master script. The cli_command will look something like: freecadcmd UpdateGeometry.py, however, I need to pass a variable into the UpdateGeometry.py script as a system argument (e.g. sys.argv[-1]). But I've looked high and low and can't seem to find any way to pass an argument into a script called in this manner.

This process is the same thing I do in my current Abaqus workflow, e.g. master script, uses subprocess.run to call a CLI executable, with an input Python script that accepts CLI arguments. The syntax for doing this with Abaqus is:

Code: Select all

abaqus cae script=try.py -- argument1 argument2 argument3
which, inside the master Python script would be something like:

Code: Select all

cli_command = f"abaqus cae script=UpdateGeometry.py -- {innerRadius} {outerRadius} {thickness}"
subprocess.run( cli_command, shell=True, check=True )
Is there any similar functionality to this when using freecadcmd that I've missed? I'd rather not have to write a temporary file to disk and hardcode this into a file read function in my script.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Is it possible to pass CLI arguments to a FreeCADCMD script?

Post by TheMarkster »

I'm not sure about subprocesses. From the command line parameters may be passed to a script as such:

Code: Select all

FreeCAD c:\users\path\to\macros\commandline_args.FCMacro "first argument" 42
And in the commandline_args macro:

Code: Select all

import sys
print(f"sys.argv = {sys.argv}")
my_arg = sys.argv[2]
my_arg2 = sys.argv[3]
print(f"my_arg = {my_arg}, my_arg2 = {my_arg2}")
Output:

Code: Select all

sys.argv = ['FreeCAD', 'c:\\users\\mwganson\\documents\\freecad\\macros\\commandline_args.FCMacro', 'first argument', '42']
my_arg = first argument, my_arg2 = 42
wmayer
Founder
Posts: 20241
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: Is it possible to pass CLI arguments to a FreeCADCMD script?

Post by wmayer »

master.py

Code: Select all

import subprocess

innerRadius = 2
outerRadius = 4
thickness = 1

cli_command = f"./bin/FreeCADCmd UpdateGeometry.py -- {innerRadius} {outerRadius} {thickness}"
subprocess.run( cli_command, shell=True, check=True )
UpdateGeometry.py:

Code: Select all

import sys
print ("import UpdateGeometry.py")

print (sys.argv)
Run it with

Code: Select all

python3 master.py
The output of UpdateGeometry.py wil be:
import UpdateGeometry.py
['./bin/FreeCADCmd', 'UpdateGeometry.py', '--', '2', '4', '1']
User avatar
sliptonic
Veteran
Posts: 3457
Joined: Tue Oct 25, 2011 10:46 pm
Location: Columbia, Missouri
Contact:

Re: Is it possible to pass CLI arguments to a FreeCADCMD script?

Post by sliptonic »

wmayer wrote: Wed Dec 01, 2021 1:04 pm ping
This works unless the argument is a filename of a type that FreeCAD understands.

For example, if I pass an argument that is a json file, I get an error from Fem:

Code: Select all

import newstepmaker.py
['/home/brad/FCD/FC/bin/FreeCADCmd', 'Script', 'newstepmaker.py', '--', 'job.json']
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/brad/FCD/FC/Mod/Fem/feminout/importYamlJsonMesh.py", line 71, in open
    return insert(filename, docname)
  File "/home/brad/FCD/FC/Mod/Fem/feminout/importYamlJsonMesh.py", line 87, in insert
    import_yaml_json_mesh(filename)
  File "/home/brad/FCD/FC/Mod/Fem/feminout/importYamlJsonMesh.py", line 132, in import_yaml_json_mesh
    femmesh = read(fileString)
  File "/home/brad/FCD/FC/Mod/Fem/feminout/importYamlJsonMesh.py", line 173, in read
    mesh_data = convert_raw_data_to_mesh_data(raw_mesh_data)
  File "/home/brad/FCD/FC/Mod/Fem/feminout/importYamlJsonMesh.py", line 190, in convert_raw_data_to_mesh_data
    for (type_key, type_dict) in raw_mesh_data.items():
<class 'AttributeError'>: 'list' object has no attribute 'items'
Exception while processing file: job.json ['list' object has no attribute 'items']


Any idea how to pass in the name of a json file as an argument to the script file?
wmayer
Founder
Posts: 20241
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: Is it possible to pass CLI arguments to a FreeCADCMD script?

Post by wmayer »

Any idea how to pass in the name of a json file as an argument to the script file?
The question is why did the above example work. Internally the numbers were treated as file names but because they don't have a suffix there is no module to handle them. That's why they "slipped" through to be handled by a script later.

With git commit f4b4aabc32 I have implemented the option "--pass" whose arguments are ignored by the application so that a script can handle them later.
So, you will have to change your master script to:

Code: Select all

json_file = "job.json"

cli_command = f"./bin/FreeCADCmd newstepmaker.py --pass {json_file}"
subprocess.run( cli_command, shell=True, check=True )
Note 1:
The option supports one or more arguments, i.e. if you write

Code: Select all

./bin/FreeCADCmd newstepmaker.py --pass job1.json job2.json job3.json
then the application ignores job1.json, job2.json, job3.json.

Note 2:
If you have a more complex command line string where some files should be handled by the application you can write

Code: Select all

./bin/FreeCADCmd newstepmaker.py --pass job1.json job2.json job3.json --input-file job4.json
or

Code: Select all

./bin/FreeCADCmd newstepmaker.py --pass job1.json job2.json job3.json -- job4.json
In this case the application ignores job1.json, job2.json, job3.json but handles job4.json
Post Reply