modelx.export_model#

export_model(model, path, use_slots=True, locked_spaces=None)[source]#

Export a given model as a self-contained Python package.

This function exports the provided model as a Python package. The resulting package is self-contained, meaning it does not require modelx.

In the resulting package, classes are defined to represent the original model and its spaces. The cells from the original model are exported as methods within these space classes.

Upon importing the generated package, an instance of the model class is created. This model can be accessed as mx_model, or under the name of the original model within the package’s namespace.

Values of types int, str, and float that are assigned to references are output as literals within the package’s modules.

Values associated with PandasData objects are saved in files within the package. The metadata from these PandasData objects are output as dictionary literals in the _mx_io.py module of the package.

Values not associated with any IOSpec objects (except for those of the types listed above) are serialized (pickled) and stored in the _mx_pickled file within the package.

Limitations

Not all modelx models can be exported. Current limitations include:

  • Relative references in ItemSpaces are not supported. All references within an ItemSpace are bound to the same objects their base references point to.

  • Objects associated with IOSpec other than PandasData, such as ExcelRange and ModuleData, are not supported.

  • modelx coerces a Cells object without parameters to its value when used as an operand of arithmetic operators (e.g., +, -, *, /). This coercion does not occur in the exported models and will be deprecated in future modelx releases. Users should ensure that such Cells are called using () in the original model’s formulas.

__slots__

The generated Space classes declare __slots__ by default. This makes the exported model faster and smaller, because CPython stores slotted attributes in a fixed-size array and specialises the bytecode that reads them. The consequences to be aware of:

  • The attributes are fixed when the model is exported, so an attribute that does not exist by then cannot be added at run time. In particular, a macro or a formula that assigns a Reference the model does not already have raises AttributeError in the exported model.

  • Space objects in the exported model are not weak-referenceable, have no __dict__, and cannot be pickled with pickle protocol 0 or 1.

  • The export raises ValueError when a name assigned as an attribute of a Space is also defined by the generated class, because __slots__ cannot declare it. In practice that is a Cells sharing its name with a parameter of its own Space or of an enclosing Space, or with a Reference, including a model-level global.

Pass use_slots=False in any of these cases to export as modelx v0.32.0 and earlier does.

Free threading

The exported model can be used from several threads at once on a free-threaded build of Python (3.13 or later built without the GIL, such as python3.14t). ItemSpaces are independent of each other, so the usual pattern is to give each thread its own range of ItemSpaces, for example Projection[i] for a range of model points. Spaces that every thread calls, such as a Space holding input data or assumptions, are not safe on their own: two threads that ask for the same Cells value before it is cached both calculate it, and a formula whose side effects are not thread-safe, such as reading a workbook, can corrupt shared state.

Pass such shared Spaces to locked_spaces to export them with a lock. A locked Space, together with the Spaces below it and its ItemSpaces, calculates each Cells value and creates each ItemSpace at most once among successful evaluations: a thread that finds a value not yet cached takes the lock shared by all the locked Spaces of the model, checks the cache again and calculates while the other threads wait. A cached value is returned without taking the lock, so the lock costs nothing once the value exists. Formulas of locked Spaces never run at the same time as each other. The lock is re-entrant, so Cells of locked Spaces can call each other and recurse freely. A formula that raises leaves no cached value and is run again by the next caller, as in modelx. Spaces not listed are exported exactly as before.

Limitations to keep in mind:

  • Lock the Spaces that several threads share and whose Cells take a bounded set of arguments. Do not lock the Space whose ItemSpaces are partitioned across the threads, such as Projection: every calculation in a locked Space waits for the one lock, and a lock that several threads wait for costs microseconds per calculation, not nanoseconds. Before starting the threads, run one ItemSpace per distinct set of shared inputs single-threaded, so that the shared values are already cached.

  • A formula of a locked Space must not wait for another thread that needs a Cells of a locked Space, for example by joining a thread or a thread pool inside the formula. It would deadlock. Keep the threads in the script that drives the model.

  • Cells whose is_cached is False are not locked. They run under the lock only while called from a locked Cells.

  • del space[args] on an ItemSpace is not safe while other threads call that Space, as before.

  • A locked Space below a parameterized Space that is not locked is copied into each of its ItemSpaces, and each copy calculates its Cells once. The export warns about such lists.

  • Space objects of a model exported with locked_spaces hold a threading.RLock and cannot be pickled or deep-copied.

  • On a build of Python with the GIL, the lock still guarantees that each value is calculated once, at the same cost.

  • Only the exported package supports threads. The modelx model it was exported from does not.

  • A package that has not declared itself free-threading compatible re-enables the GIL when imported, with a warning. pandas 2 is one; run Python with the environment variable PYTHON_GIL=0 to keep the GIL disabled in that case.

  • Compiling a model exported with locked_spaces requires modelx-cython v0.1.0 or later.

Parameters:
  • model – The Model object to be exported.

  • path – The path where the generated Python package will be located.

  • use_slots (bool, optional) – Whether the generated Space classes declare __slots__. Defaults to True.

  • locked_spaces (optional) – Spaces to export with the lock described above, as an iterable of Space names relative to the model ("Parent.Child" for a nested Space) or Space objects. Each listed Space is locked together with the Spaces below it. Defaults to None, which locks no Space.

See also

export()

Added in version 0.22.0.

Changed in version 0.33.0: The use_slots and locked_spaces parameters are added, and the generated Space classes declare __slots__ by default.