Pragma directives in Python?
-
Are there any pragma directives in Python to be able to, for example, stop some lines of code to be included when the "Source Protector..." command is executed on a .pyp file?
-
Hello @rui_mac,
thank you for reaching out to us. I am a bit confused by your question.
#pragma
is a pre-processor directive and Python has no pre-processor in that sense. But you can of course put conditional statements into the the module scope, or code in general, which then can sort of work like pre-processor directives.import c4d import sys # In C/C++ we would solve something like this with the pre-processor if sys.version_info.major == 3: pass if c4d.GetC4DVersion() >= 26000: pass class MyFancyPlugin(...): ...
A '*.pyp' file is nothing special and does not differ from a common python module. The different file extension primarily does exist so that Cinema knows where to enter a (set of) plugin(s). If you wanted to, you could find out if code is being executed from a '*.pyp' file by looking at the
__file__
attribute of an executed module. E.g.:import c4d print (__file__)
Last but not least, you cannot detect the source protector being executed, because the source protector does not run any code. I am not sure, but it might be that you are confusing here byte-code compilation and source protection. This has been discussed before here. Source protection is just something that happens before Python code is being interpreted and compiled. It does not care about the content of the file, you could protect your favorite cooky recipes with it, and simply encrypts the content of a file with AES. Before the file is being compiled to byte code, to be loaded by the Python VM, Cinema 4D will decrypt it.
If you want some kind of self modifying code on encryption, I would reccomend using a Python script which wraps the
g_encryptPypFile
of Cinema 4D apps, e.g.,c4dpy
as shown in the thread linked above. So, you have some scriptdo_encrypt.py
which takes another fileencrypt_me.py
as an argument. It will then runc4dpy
withencrypt_me.py
as the encryption argument. If you wantedencrypt_me.py
to be modified to some extent, you could:- Just put that into
do_encrypt.py
, or - Have an
__encrpyt__
execution context inencrypt_me.py
which modifies the file, so thatdo_encrypt.py
can just execute that context for all modules it is encrypting.
Cheers,
Ferdinand - Just put that into
-
Thank you for the answer.
My purpose was quite simple, I guess. Let me try to explain.
My plugins have a protection scheme that check something online and do some time period calculations. But, during development, I don't want to keep checking for all these. I just want that all those things get executed when the final version of the code is executed, when the person who bought the plugin has it running on their machine.
So, what I was hopping what to have a way to code something like (and this is completely made up..):#ifnotprotected
return TrueYes, I know the # character is a comment in Python, but let us imagine the Source Protector code checked for keywords directly following the #.
So, if there was a way to include code in the Python code that WAS NOT INCLUDED in the final encrypted file that is produced by the Source Protector but is executed when the code is still being developed (or vice-versa, optionally), that would be great.
There is no such thing, is it? -
Hey @rui_mac,
thank you for the clarification. The major obstacle seems to be here faciality with Python and that I consider things obvious that are not that obvious. So let me be verbose here (go to point 4 for TLDR):
- Python does not have any pre-processor like logic in the more concrete sense because to Python the whole C-thing of "if this applies, compile this into something entirely different" does not apply, because Python simply does not work like this, there is no strict distinction of platforms or build versions in Python.
- CPython, i.e., our interpreter, has a debug attribute, exposed as
__debug__
in all modules. By default, this is alwaystrue
, so all code is by default released in debug mode in Python. You can hook in that attribute to distinguish between a debug and release version. But you should be aware that setting__debug__
toFalse
will also impact other things, e.g., setting it toFalse
will skip all assertions. In the course of writing this answer I found out that there is a bug in our interpreter as it ignores the-O
argument. Since__debug__
is read-only without getting very hacky,__debug__ = True
, you effectively cannot use this feature inc4dpy
. There are also other problems with this I will not go into here. We will fix this in an upcoming release.
# Will be #False when the -0 argument is being passed and #True when not. print (f"{__debug__ = }") # Will do nothing when the -0 argument is being passed and raise an AssertionError when not. assert(True is False)
- The other mechanism wich Python has that is somewhat similar to the idea of build versions, is the idea of execution contexts of a module, expressed through its __ name __ attribute. E.g.;
# The standard module execution context to distinguish a file from being loaded # from being executed. if __name__ == "__main__": pass # Something like this could also be conceivable, it is just that we, our # Python interpreter, would have to call Python plugin (*.pyp) modules in such # manner for this to work. You cannot do this yourself. # Could be conceived to be called when Cinema 4D is being started with a # fictitious debug argument. if __name__ == "__debug__": pass
- The only thing that remains for you is to simply define a debug module attribute of your own.
import os IS_DEBUG: bool = True # Defines the debug state of the plugin. # A pattern which is sometimes used in Python, is to make a debug state # dependent on the existence of a file, usually a log file. The reasoning # is here that it is harder to overlook a file in a release package than # some relatively hidden module attribute. HAS_DEBUG_LOG: bool = os.path.exists( os.path.join(os.path.dirname(__file__), "debug.log")
Cheers,
Ferdinand -
Once again, thenk you very much for all the information, Ferdinand.
What I had done already was the last "solution".
I do have a file inside the plugin folder that, when the code detects that it is present, skips some verifications.
So, I guess I will go on doing that. That even allows me to check for specific filenames and perform different actions for each one.
I was just checking to see if there was any type of build-in mechanism to emulate that pragma directives did.
And I do know that python is interpreted and that real pragma directives are evaluated in the prepass stage.
But I didn't knew if the Source Protector performed some type of evaluation, when protecting the code. -