python-oxidized-importer/docs/oxidized_importer_freezing_applications.rst
.. py:currentmodule:: oxidized_importer
.. _oxidized_importer_freezing:
oxidized_importeroxidized_importer can be used to create and run frozen Python
applications, where Python resources data (module source and bytecode,
etc) is frozen/packaged and distributed next to your application.
This is conceptually similar to what PyOxidizer does. The major
difference is that PyOxidizer will package and distribute a Python
distribution with your application: when only oxidized_importer is being
used, the Python distribution is provided by some other means (it is
typically already installed on the system). This makes oxidized_importer
a light-weight alternative to PyOxidizer for scenarios where PyOxidizer
isn't suitable or viable.
The steps for freezing an application all look the same:
OxidizedResource instances into an :py:class:OxidizedFinder
instance so they are indexed.OxidizedFinder instance
and load the resources blob you generated.OxidizedFinder instance as the first element on
sys.meta_path.The next sections show what this may look like.
.. _oxidized_importer_freezing_build:
In your build process, you'll need to index resources and serialize
them. You can construct OxidizedResource instances directly and hand
them off to an :py:class:OxidizedFinder instance. But you'll probably
want to use OxidizedResourceCollector to make this simpler.
Try something like the following:
.. code-block:: python
import os import stat import sys
import oxidized_importer
collector = oxidized_importer.OxidizedResourceCollector( allowed_locations=["in-memory"] )
for path in sys.path: # Only directories can be scanned by oxidized_importer. if os.path.isdir(path): for resource in oxidized_importer.find_resources_in_path(path): collector.add_in_memory(resource)
OxidizedResource and fileresources, file_installs = collector.oxidize()
finder = oxidized_importer.OxidizedFinder() finder.add_resources(resources)
packed_data = finder.serialize_indexed_resources()
with open("oxidized_resources", "wb") as fh: fh.write(packed_data)
for (path, data, executable) in file_installs: path.parent.mkdir(parents=True, exist_ok=True)
with path.open("wb") as fh:
fh.write(data)
if executable:
path.chmod(path.stat().st_mode | stat.S_IEXEC)
At this point, you've collected all known Python resources and written out a data structure describing them all. For resources targeting in-memory loading, the content of those resources is embedded in the data structure. For resources targeting filesystem-relative loading, the data structure contains the relative path to those resources. And you've written out the files in the locations where those relative paths point to.
Now, from our application code, we need to load the resources and register the custom importer with Python:
.. code-block:: python
import os import sys
import oxidized_importer
finder = oxidized_importer.OxidizedFinder() finder.index_file_memory_mapped("oxidized_resources")
python3 executable),origin to the directory the resources arefinder = oxidized_importer.OxidizedFinder( relative_path_origin=os.path.dirname(os.path.abspath(file)), ) finder.index_bytes(packed_data)
sys.meta_path.insert(0, finder)
import modules defined