https://docs.blender.org/api/current/bpy.ops.html#overriding-context Seems to clarify enough:
Overriding Context
It is possible to override context members that
the operator sees, so that they act on specified rather than the
selected or active data, or to execute an operator in the different
part of the user interface.
The context overrides are passed as a dictionary, with keys matching
the context member names in bpy.context. For example to override
bpy.context.active_object, you would pass {'active_object': object} to
bpy.types.Context.temp_override.
Note
You will nearly always want to use a copy of the actual current
context as basis (otherwise, you’ll have to find and gather all needed
data yourself).
# Remove all objects in scene rather than the selected ones.
import bpy
from bpy import context
override = context.copy()
override["selected_objects"] = list(context.scene.objects)
with context.temp_override(**override):
bpy.ops.object.delete()
So... we don't know what context should be used. Luckily that context is right when a script is run from text editor so if I just print current context with a script in the text editor that's the right context:
import bpy
current_context = bpy.context.copy()
print(current_context)
That gives me this in Blender's system console:
{'active_annotation_layer': None, 'active_bone': None, 'active_editable_fcurve': None, 'active_gpencil_frame': None, 'active_gpencil_layer': None, 'active_nla_strip': None, 'active_nla_track': None, 'active_object': None, 'active_operator': None, 'active_pose_bone': None, 'active_sequence_strip': None, 'annotation_data': None, 'annotation_data_owner': None, 'area': bpy.data.screens['Layout']...Area, 'asset_file_handle': None, 'asset_library_ref': bpy.data.workspaces['Main']...AssetLibraryReference, 'blend_data': <bpy_struct, BlendData at 0x0000026E1ED44108>, 'collection': bpy.data.collections['Main'], 'edit_object': None, 'edit_text': bpy.data.texts['Text'], 'editable_bones': None, 'editable_fcurves': None, 'editable_gpencil_layers': None, 'editable_gpencil_strokes': None, 'editable_objects': [], 'engine': 'CYCLES', 'evaluated_depsgraph_get': <bpy_func Context.evaluated_depsgraph_get()>, 'gizmo_group': None, 'gpencil_data': None, 'gpencil_data_owner': None, 'image_paint_object': None, 'layer_collection': bpy.data.scenes['Scene']...LayerCollection, 'mode': 'OBJECT', 'object': None, 'objects_in_mode': [], 'objects_in_mode_unique_data': [], 'particle_edit_object': None, 'pose_object': None, 'preferences': <bpy_struct, Preferences at 0x00007FF628D8D8E0>, 'region': bpy.data.screens['Layout']...Region, 'region_data': None, 'scene': bpy.data.scenes['Scene'], 'screen': bpy.data.screens['Layout'], 'sculpt_object': None, 'selectable_objects': [], 'selected_bones': None, 'selected_editable_actions': None, 'selected_editable_bones': None, 'selected_editable_fcurves': None, 'selected_editable_keyframes': None, 'selected_editable_objects': [], 'selected_editable_sequences': [], 'selected_movieclip_tracks': None, 'selected_nla_strips': None, 'selected_objects': [], 'selected_pose_bones': None, 'selected_pose_bones_from_active_object': None, 'selected_sequences': [], 'selected_visible_actions': None, 'selected_visible_fcurves': None, 'sequences': [], 'space_data': bpy.data.screens['Layout']...SpaceTextEditor, 'tool_settings': bpy.data.scenes['Scene'].tool_settings, 'ui_list': None, 'vertex_paint_object': None, 'view_layer': bpy.data.scenes['Scene'].view_layers["View Layer"], 'visible_bones': None, 'visible_fcurves': None, 'visible_gpencil_layers': None, 'visible_objects': [], 'visible_pose_bones': None, 'weight_paint_object': None, 'window': bpy.data.scenes['Scene']...Window, 'window_manager': bpy.data.window_managers['WinMan'], 'workspace': bpy.data.workspaces['Main']}
OK, so let's look for something interesting there. It's probably none of the stuff that is None
... so I ignore that and look through all the other stuff and here it is 'edit_text': bpy.data.texts['Text']
- now that looks really interesting. Let's try that:
import bpy
from bpy import context
override = context.copy()
override["edit_text"] = bpy.data.texts['script_to_run.py']
with context.temp_override(**override):
bpy.ops.text.run_script()
And it works! That's it.