Running commanline application with Python
-
Hi guys,
Running into a bit of an issue with the commandline application on Windows/MacOS when running and "driving" it via Python (standalone, outside Cinema4D).
We're writing an application that automatically handles the license logins on the machines in our renderfarms. Every time there is a minor update to Cinema or the Maxon App, all our machines seem to lose their license which is a lot of manual labor to get working again.
We have the following code:
import subprocess import os from os.path import expanduser import time # Petermine the platform (Windows or MacOS) is_windows = os.name == 'nt' # Path to your C4D executable if is_windows: try: c4d_executable = r'C:\Program Files\Maxon Cinema 4D 2023\Commandline.exe' except: print("Commandline executablecould not be found!") subprocess.wait(5) else: try: c4d_executable = '/Applications/Maxon Cinema 4D 2023/Commandline.app/Contents/MacOS/Commandline' except: print("Commandline executablecould not be found!") subprocess.wait(5) # Define the license information to provide license_method = "2" license_username = "XXXXXXXX" license_password = "XXXXXXXX" license_floating_commandline = '1' # Define license files # Get user home directory home = expanduser("~") print(home) # Create a subprocess to run C4D c4d_process = subprocess.Popen( [c4d_executable], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True ) # Use a loop to read and print the output in real-time def capture_and_print_output(process): while True: output_line = process.stdout.readline() if not output_line: break # No more output, exit the loop print(output_line, end='') # Print the line without adding extra newline # Check for error messages in the output if "Error:" in output_line: print("Error message detected:", output_line) while True: error_line = process.stderr.readline() if not error_line: break # No more error output, exit the loop print("Error:", error_line, end='') # Capture and print outputs before providing license information capture_and_print_output(c4d_process) # Output before license input # Wait for Commandline to prompt for input output, _ = c4d_process.communicate(timeout=10) print("Output:", output) if "Please select:" in output: print("Inside the license method loop") # Provide the license information time.sleep(5) c4d_process.stdin.write(license_method + '\n') c4d_process.stdin.flush() print("sleeping") time.sleep(2) # Capture and print outputs after providing license method capture_and_print_output(c4d_process) # Send the Enter key c4d_process.stdin.write('\n') c4d_process.stdin.flush() print("sleeping after Enter") time.sleep(2) # Capture and print outputs after providing license method capture_and_print_output(c4d_process) time.sleep(2) # Capture and print outputs after providing license method capture_and_print_output(c4d_process) if "Account email address []: " in output: print("Inside the email address loop") # Provide the license information c4d_process.stdin.write(license_username) c4d_process.stdin.flush() # Send the Enter key c4d_process.stdin.write('\n') c4d_process.stdin.flush() # Capture and print outputs after providing email address capture_and_print_output(c4d_process) if "Password: " in output: print("Inside the password loop") # Provide the license information c4d_process.stdin.write(license_password) c4d_process.stdin.flush() # Send the Enter key c4d_process.stdin.write('\n') c4d_process.stdin.flush() # Capture and print outputs after providing password capture_and_print_output(c4d_process) if "Please choose one of the following options: " in output: print("Inside the license type loop") # Provide type of license c4d_process.stdin.write(license_floating_commandline) c4d_process.stdin.flush() # Send the Enter key c4d_process.stdin.write('\n') c4d_process.stdin.flush() # Capture and print outputs after providing license type capture_and_print_output(c4d_process) else: print("No input detected!") c4d_process.wait(5) c4d_process.stdin.close() c4d_process.stdout.close() c4d_process.stderr.close() c4d_process.terminate() # You can now continue working with the C4D process or wait for it to finish print("Licensing process ran succesfully") c4d_process.wait(5) # Close the subprocess and clean up c4d_process.stdin.close() c4d_process.stdout.close() c4d_process.stderr.close() c4d_process.terminate()
Basically what happens is that the commandline application doesn't seem to register the "replies" that Python gives it when it asks for specific license information inputs. I've tried a ton of different ways to go about it but none seem to work.
Tried with sleep, without sleep, "Please select: ", "Please select:", "Enter the license method:", "Enter the license method: ", with \n, without \n etc.Is there some specific timing I need to adhere to or something else behind the scenes that I'm not aware of?
Kind regards
-
Hello @Peek,
Thank you for reaching out to us. I will answer here within this week, but I must deliberate with my colleagues first, as I am unsure about both legal and technical aspects of what you are trying to do here.
Cheers,
Ferdinand -
@ferdinand : Hi Ferdinand, thanks for getting back to me on this.
As for the legal aspects (the following is my personal opinion), what do you mean exactly?
We pay for licenses and we don't want the inconvenience of having to manually go through all render clients when the licensing system fails us yet again.
These actions (inputting license information on all commandline clients in our farm) cost man hours which can be more effectively spent working for clients rather than working around a needlessly cumbersome system.
There is no effective difference between me manually inputting the information on a client and doing it in an automated fashion with a piece of code.That being said, if I'm missing something else here please feel free to correct my thinking.
Cheers,
Joep -
Hello @Peek,
I understand your motivation and it is understandable. The legal aspect is not about you owning the licenses or not. For further details, I would still have to ask you to wait until I have talked with my colleagues.
Cheers,
Ferdinand -
@ferdinand : I will, thanks again for investigating
Cheers,
Joep -
Hey @Peek,
so, after some digging, here is how you can do this.
- In general, we cannot support interacting with our logins by emulating user inputs.
- But you can use the command line arguments which are documented here and in more detail here.
- There is/was a missing puzzle piece to all this in form of
g_licenseModel
, which before has been a private argument and therefore cannot be found in the technical SDK documentation I linked to above or the command line user documentation. I have documented the argument below, and we will also expose it in the technical and user documentation (in fact a few things should be updated there).
Note: This thread was unrelated to the Cinema 4D SDK (and therefore technically out of scope of support). Please post such questions in General Talk in the future. I have moved this thread. And while it is understandable that you were seeking support in this case, the SDK group cannot provide support for generic pipelining issues. You should reach out to end user support for them.
Cheers,
FerdinandCode:
"""Demonstrates how to login into any Cinema 4D application (Cinema 4D, c4dpy, commandline, Team Render) using command line arguments. """ import os import subprocess def run_c4d( appPath: str, filePath: str, args: tuple[str] = ()) -> subprocess.CompletedProcess: """Runs a Cinema 4D app at the given #appPath with the given command line #args. Optionally one can pass a #filePath to load a file into the app (*.c4d, *.py, *.bmp, etc.). """ if not os.path.exists(appPath) or not os.path.isfile(appPath): raise OSError(f"The path '{appPath = }' does not exist or is not a file.") if filePath != "" and (not os.path.exists(filePath) or not os.path.isfile(filePath)): raise OSError(f"The path '{filePath = }' does not exist or is not a file.") return subprocess.run([appPath, filePath] + list(args)) def main() -> None: """Runs the example. """ # Some example apps to run. cmdlineApp: str = "c:\\program files\\maxon\\2023.2.2\\commandline.exe" c4dpyApp: str = "c:\\program files\\maxon\\2023.2.2\\c4dpy.exe" # Provide our credentials for a Maxon Account login. This only works with non-federated accounts, # i.e., logins that are not forwarded to Apple, Google, or Facebook. Note that Maxon Account as # a login model has been deprecated in 2024. In the GUI of the Cinema 4D license manager it has # been removed altogether, in the terminal it is still available. userLogin: list[str] = [ # The license model we are using. # Other values for LICENSEMODEL: # ::LICENSESERVER - Maxon License Server # ::RLM - RLM License Server # ::MAXONAPP - Maxon App "g_licenseModel=LICENSEMODEL::MAXONACCOUNT", # The credentials. "[email protected]", "g_licensePassword=password1234" ] # Run the commandline app. res: subprocess.CompletedProcess = run_c4d( cmdlineApp, "", userLogin) if (res.stderr): raise RuntimeError(f"Terminated on process error: {res.stderr}") # Run the c4dpy app. res: subprocess.CompletedProcess = run_c4d( c4dpyApp, "c:\\users\\f_hoppe\\desktop\\my_script.py", userLogin) if (res.stderr): raise RuntimeError(f"Terminated on process error: {res.stderr}") if __name__ == "__main__": main()
-
-
Hi @ferdinand : Thanks a ton for helping out even though technically this kind of support is outside the scope.
I will read up on the material you provided and implement the code example into our own code.
As always you've been very helpful
Cheers,
Joep
-
@ferdinand Due to the busy schedule at work I've only now gotten around to getting to implement this and I'm a bit stuck.
- When I run both the commandline and c4dpy the terminal goes into some kind of endless loop on one of the MacOS rendernodes, it just keeps going endlessly with the same wall of text.
- When I run just the commandline part it ends, but the license info doesn't seem to be inputted. As when I then manually run the commandline afterwards it asks for license info again.
- When I run just the c4dpy part it also goes into an endless loop spouting the same wall of text over and over again.
You mentioned in your reply "and we will also expose it in the technical and user documentation (in fact a few things should be updated there)." but I can't find anything in the SDK documentation about "g_license", just one quick mention in c4dpy about passing username and password as arguments.
-
Hello @Peek,
Regarding the docs, I simply forgot to add stuff on the SDK side. Thank you for the hint, I will add it now.
Regarding your problems with logging in: My script just automates some shell commands and there is no difference between the apps (c4d, cmdline, c4dpy, t-server, t-client) regarding login, when it works with one, it will work with all. I just tested my script, and for me it still works with
2023.2.2
and also in2024.2.0
. Here I ran c4dpy with a script printingHello World!
:To get a better sense of what is going wrong, just run the following command in the shell of your choice after filling in your data:
[PATH] g_licenseModel=LICENSEMODEL::MAXONACCOUNT g_licenseUsername=[HANDLE] g_licensePassword=[PASSWORD] e.g., c:\"program files"\maxon\2024.2.0\commandline.exe g_licenseModel=LICENSEMODEL::MAXONACCOUNT [email protected] g_licensePassword=MyFancyPassword
When your login fails (wall of text), it either means your credentials are invalid or that you have a federated login. A federated login is any login that redirects you away from our
id.maxon.net
plugin page to a third party page to fill in your credentials. The usual suspects are here Apple, Google, and Facebook:But there are also some non-public federated logins, if you have one you should know. As explained in my first posting, federated logins are simply not supported by the terminal logins, it cannot deal with the redirects.
And as also already stated, please contact end-user-support about any issues you might have with logins, this is simply not an SDK issue.
Cheers,
Ferdinand -
Thanks for adding the SDK info and also thanks for the hints, I will investigate further. We normally log in to Maxon accounts via the commandline or via the login window in Cinema, so it should work I imagine.
-
Hey @Peek,
The terminology is here a bit unfortunate, and I was also a bit sloppy with it in my posting (fixed that). Let us make a clear distinction between the 'Commandline' (the Cinema 4D app, not as a term for the shell of an OS) and the
terminal
orshell
of an OS (e.g.,CMD
.PowerShell
on Windows, or justShell
on Mac). If you are already regularly logging in via the shell/terminal, this script should work.But if that would be the case, you probably would not have asked this question, started this topic. As I said, I would just try to manually login with the command I have shown above. And if that does not work, reach out to our end user support. I would also remind you to what I pointed out in the script:
Note that Maxon Account as a login model has been deprecated in 2024. e, Google, or Facebook. In the GUI of the Cinema 4D license manager it has been removed altogether, in the terminal it is still available.
For federated accounts the terminal login never worked, but maybe they have added more restrictions since 2023.2, blocked certain IP ranges or god knows what else. End user support can help you here
Cheers,
Ferdinand -
@ferdinand When I was talking about Commandline I mean the actual Cinema4D app/.exe, not the terminal/powershell.
I will give the command a go Today and see how it goes. Thanks for the help regardless that it was outside of scope
Well, with your command on MacOS I basically get a wall of text but it does finish. However when I then manually run the C4D commandline again I get the standard "Enter the license method:" window
When I run it in c4dpy it ends on "Welcome to the world of C4D and python (....)" and >>>.
Cheers,
-
Hey @Peek,
when the command does fail for you, this probably means that there is either something wrong with your account/credentials or that the terminal login is broken for the Commandline app on MacOS. I find it a bit odd that you seem to indicate that the login works for the c4dpy app on MacOs but for the Commandline app it does not.
This is how it will look like when you try to login with a federated account and the terminal login then failing:
You should reach out to user support
Cheers,
Ferdinand -
@ferdinand : Yeah I will because that is definitely not what I'm seeing
Manually login in in both commandline/c4dpy works. As that is what we are doing now on all render nodes when the licenses vanish yet again.
Thanks!
-
One last thing: Have you tried using LICENSEMODEL::MAXONAPP as an alternative login method? ::MAXONACCOUNT is being deprecated as I said, and I do not quite remember why we chose it here nonetheless. User support surely can help you with the details here.