=============================== Understanding Obfuscated Script =============================== .. highlight:: console .. program:: pyarmor gen **Remain as standard .py files** The obfuscated scripts are normal Python scripts, it's clear by checking the content of :file:`dist/foo.py`: .. code-block:: python :linenos: from pyarmor_runtime_000000 import __pyarmor__ __pyarmor__(__name__, __file__, b'\xa...') It's a simple script, first imports function ``__pyarmor__`` from package ``pyarmor_runtime_000000``, then call this function. **Runtime package** This package ``pyarmor_runtime_000000`` is generated by Pyarmor, it's also a normal Python package, here it's package content:: $ ls dist/pyarmor_runtime_000000 ... __init__.py ... pyarmor_runtime.so There is binary `extension module`_ :mod:`pyarmor_runtime`, this is a big difference from plain Python script. Generally using binary extensions means the obfuscated scripts * may not be compatible with different builds of CPython interpreter. * often will not work correctly with alternative interpreters such as PyPy, IronPython or Jython For example, when obfuscating scripts by Python 3.8, they can be run by any Python 3.8.x, but can't be run by Python 3.7, 3.9 etc. For example, packaging pure `.py` script is easy, but packaging binary extension need more work. For example, in Android pure `.py` script can be run in any location, but binary extensions must be in special system paths. The runtime package ``pyarmor_runtime_000000`` could be in any path, it can be taken as a third-party package, save it in any location, and import it following Python import system. pyarmor provides several options :option:`-i`, :option:`--prefix` to help generating right code to import it. Also refer to option :option:`--use-runtime` and command :ref:`pyarmor gen runtime` for using shared runtime package. .. If obfuscated scripts could not import this package, failures in this step may indicate either that this runtime package could not be located, or that an error occurred while initializing runtime package. .. seealso:: `Changing runtime package name` in the chapter :doc:`../tutorial/customization` `Using shared runtime package` in the chapter :doc:`../tutorial/advanced` **Runtime key** The runtime key generally is embedded into extension module ``pyarmor_runtime``, it also could be an outer file. It stores expire date, bind devices, and user private data etc. Extension module ``pyarmor_runtime`` will not load the obfuscated script unless the runtime key exists and is valid. User also could store any private data in the runtime key, then use :term:`hook script` to check private data in the obfuscated scripts. If runtime key is stored in an outer file, any readable text in the header will be ignored. User can add comment at the header of runtime key file, the rest part are bytes data, only in the obfuscated scripts they could be read. .. seealso:: command :ref:`pyarmor gen key` .. _restrict modes: Restrict modes ============== By default the obfuscated scripts can't be changed. After using :option:`--private`, the attributes of the obfuscated scripts could not be seen by plain script or Python interpreter. After using :option:`--restrict`, the attributes of the obfuscated scripts are protected as :option:`--private`, and the obfuscated scripts could not be imported by plain script or Python interpreter, Disable all the restrictions by this command:: $ pyarmor cfg restrict_module 0 Generally only disable all the restrictions for specified module. For example, only no restrictions for module ``NAME``:: $ pyarmor cfg -p NAME restrict_module 0 .. _the differences of obfuscated scripts: The differences of obfuscated scripts ===================================== Although **use obfuscated scripts as they're normal Python scripts**, but the obfuscated scripts are still different from pure Python scripts, they changes a few Python features and results in some third party packages could not work. Here are major changed features: * The obfuscated scripts are bind to Python major/minor version. For example, if it's obfuscated by Python 3.6, it must run by Python 3.6. It doesn't work for Python 3.5 * The obfuscated scripts are platform-dependent, supported platforms and Python versions refer to :doc:`../reference/environments` * If Python interpreter is compiled with Py_TRACE_REFS or Py_DEBUG, it will crash to run obfuscated scripts. * Any module may not work if it try to visit the byte code, or some attributes of code objects in the obfuscated scripts. For example most of ``inspect`` function are broken. * Pass the obfuscated code object by ``cPickle`` or any third serialize tool may not work. * ``sys._getframe([n])`` may get the different frame. Note that many third packages uses this feature to get local variable and broken. For example, ``pandas``, ``cherrypy``. * The code object attribute ``__file__`` is ```` other than real filename. Note that module attribute ``__file__`` is still filename. For example, obfuscate the script ``foo.py`` and run it: .. code-block:: python def hello(msg): print(msg) # The output will be 'foo.py' print(__file__) # The output will be '' print(hello.__file__) A few options may also change something: - ``pyarmor cfg mix_argname=1`` hides annotations. - Importing obfuscated module by `importlib.util.spec_from_file_location` need extra handle, refer to `issue 846`__ .. seealso:: :doc:`../how-to/third-party` `Generating cross platform scripts` and `Obfuscating scripts for multiple Python versions` in the chapter :doc:`../tutorial/advanced` __ https://github.com/dashingsoft/pyarmor/issues/846 Supported Third-Party Interpreter ================================= About third-party interpreter, for example Jython, and any embedded Python C/C++ code, only they could work with CPython :term:`extension module`, they could work with Pyarmor. Check third-party interpreter documentation to make sure this. A few known issues * On Linux, `RTLD_GLOBAL` must be set as loading `libpythonXY.so` by `dlopen`, otherwise obfuscated scripts couldn't work. * Boost::python does not load `libpythonXY.so` with `RTLD_GLOBAL` by default, so it will raise error "No PyCode_Type found" as running obfuscated scripts. To solve this problem, try to call the method `sys.setdlopenflags(os.RTLD_GLOBAL)` as initializing. * `PyPy` could not work with pyarmor, it's total different from `CPython` * WASM is not supported. .. seealso:: :ref:`target environments` .. include:: ../_common_definitions.txt