Skip to content

Commit 40d03b2

Browse files
committed
gh-76: Improve calling pymhf from code and document
1 parent 441d175 commit 40d03b2

File tree

13 files changed

+120
-24
lines changed

13 files changed

+120
-24
lines changed

docs/docs/change_log.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Current (0.1.16.dev)
88
- Fixed an issue running mods where attach to already existing processes.
99
- Fixed an issue with getting the caller offset address.
1010
- Added the ability to specify an already running processes' PID to attach pyMHF. (`#75 <https://github.com/monkeyman192/pyMHF/issues/75>`_)
11+
- Made :py:func:`~pymhf.main.run_module` a public method as the recommended way to invoke pyMHF from code. (`#76 <https://github.com/monkeyman192/pyMHF/issues/76>`_)
1112

1213
0.1.15 (16/07/2025)
1314
-------------------

docs/docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Usage guide
44
.. toctree::
55
:maxdepth: 1
66

7+
usage
78
settings
89
writing_mods
910
creating_hook_definitions

docs/docs/inter_mod_functionality.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Running multiple mods in a single folder
8888
----------------------------------------
8989

9090
Similar to how single-file mods work. pyMHF can be pointed to a folder to run (ie. the path you provide to the ``pymhf run`` command is the folder.)
91-
Currently, for this to work the folder must contain the ``pymhf.toml`` file as if it were a library (see the [settings](../settings.md) docs for more details).
91+
Currently, for this to work the folder must contain the ``pymhf.toml`` file as if it were a library (see :doc:`/docs/settings` for more details).
9292

9393
Caveats / Things to keep in mind
9494
--------------------------------

docs/docs/settings.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ This section handles properties which relate to the game or program that the lib
2222
``exe``
2323
"""""""
2424

25+
*Required unless ``pid`` specified*
26+
2527
If :ref:`settings-pymhf.start_exe` is ``True`` (the default) - either the absolute path to the binary being run, or the name of the exe which is being run by steam.
2628

2729
If :ref:`settings-pymhf.start_exe` is ``False`` - the exe name (ie. ``notepad.exe``) of an already running process to attach to.
@@ -31,6 +33,8 @@ If :ref:`settings-pymhf.start_exe` is ``False`` - the exe name (ie. ``notepad.ex
3133
``pid``
3234
"""""""
3335

36+
*Optional*
37+
3438
The process id to attach pyMHF to. This will have no effect if :ref:`settings-pymhf.exe` is specified and also if :ref:`settings-pymhf.start_exe` is False.
3539

3640
.. _settings-pymhf.steam_guid:

docs/docs/usage.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Usage
2+
=====
3+
4+
There are a number of convenient ways to use pyMHF:
5+
6+
Command line
7+
------------
8+
9+
pyMHF registers ``pymhf`` as a command which can be called from your shell. This command has a number of options (call ``pymhf --help`` for more details).
10+
11+
From code
12+
---------
13+
14+
pyMHF may also be invoked from code in the case of a more complex situation (eg, needing to hook some dynamically generated subprocess).
15+
16+
.. code-block:: py
17+
18+
import os.path as op
19+
from subprocess import Popen
20+
21+
from pymhf import run_module
22+
23+
if __name__ == "__main__":
24+
# Run a mod by creating the process and then attaching pymhf later.
25+
proc = Popen("notepad.exe")
26+
CONFIG = {
27+
"pid": proc.pid,
28+
"start_paused": False,
29+
"start_exe": False,
30+
}
31+
run_module(op.join(op.dirname(__file__), "test_mod.py"), CONFIG, None, None)
32+
33+
In the above we are passing the absolute path to the file (or it could be mod folder) to be loaded, as well as the explicit configuration to be loaded.
34+
35+
.. note::
36+
See :doc:`/docs/settings` for details on the keys which the config can have. Keep in mind that the keys under the ``pymhf`` section are top level keys, and keys under the subsections (eg. ``pymhf.logging``) will have a top-level key of ``logging`` and then the keys are under that.
37+
38+
.. note::
39+
See :py:func:`~pymhf.main.run_module` for details on the other arguments (they are only required if you are calling a library which is VERY unlikely from code...)

examples/run_mod_from_code.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import os.path as op
2+
from subprocess import Popen
3+
4+
from pymhf import run_module
5+
6+
if __name__ == "__main__":
7+
# Run a mod by creating the process and then attaching pymhf later.
8+
# It is generally recommended that you just use pyMHF's built-in functionality for doing this.
9+
proc = Popen("notepad.exe")
10+
CONFIG = {
11+
"pid": proc.pid,
12+
"start_paused": False,
13+
"start_exe": False,
14+
}
15+
run_module(op.join(op.dirname(__file__), "test_mod.py"), CONFIG, None, None)

examples/test_mod.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# ///
1717
from logging import getLogger
1818

19-
from pymhf import Mod
19+
from pymhf import Mod, load_mod_file
2020

2121
logger = getLogger("TestMod")
2222

@@ -28,3 +28,7 @@ class TestMod(Mod):
2828
def __init__(self):
2929
super().__init__()
3030
logger.info("Loaded Test mod!")
31+
32+
33+
if __name__ == "__main__":
34+
load_mod_file(__file__)

pymhf/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .core._types import FUNCDEF # noqa
1414
from .core.hooking import FuncHook # noqa
1515
from .core.mod_loader import Mod, ModState # noqa
16-
from .main import load_mod_file, load_module # noqa
16+
from .main import load_mod_file, load_module, run_module # noqa
1717
from .utils.imports import SPHINX_AUTODOC_RUNNING
1818

1919
try:

pymhf/core/_internal.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import ctypes
22
from concurrent.futures import ThreadPoolExecutor
3-
from enum import Enum
4-
5-
6-
class LoadTypeEnum(Enum):
7-
INVALID = 0
8-
SINGLE_FILE = 1
9-
LIBRARY = 2
10-
MOD_FOLDER = 3
113

4+
from pymhf.core._types import LoadTypeEnum
125

136
CWD: str = ""
147
MODULE_PATH: str = ""

pymhf/core/_types.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,40 @@
11
from enum import Enum
22
from typing import Any, NamedTuple, Optional, Protocol
33

4+
from typing_extensions import NotRequired, TypedDict
5+
6+
7+
class pymhfGUIConfig(TypedDict):
8+
shown: NotRequired[bool]
9+
scale: NotRequired[float]
10+
always_on_top: NotRequired[bool]
11+
12+
13+
class pymhfLoggingConfig(TypedDict):
14+
default_log_dir: NotRequired[str]
15+
log_level: NotRequired[str]
16+
window_name_override: NotRequired[str]
17+
18+
19+
class pymhfConfig(TypedDict):
20+
exe: NotRequired[str]
21+
pid: NotRequired[int]
22+
steam_guid: NotRequired[int]
23+
required_assemblies: NotRequired[list[str]]
24+
start_paused: NotRequired[bool]
25+
default_mod_save_dir: NotRequired[str]
26+
internal_mod_dir: NotRequired[str]
27+
start_exe: NotRequired[bool]
28+
logging: NotRequired[pymhfLoggingConfig]
29+
gui: NotRequired[pymhfGUIConfig]
30+
31+
32+
class LoadTypeEnum(Enum):
33+
INVALID = 0
34+
SINGLE_FILE = 1
35+
LIBRARY = 2
36+
MOD_FOLDER = 3
37+
438

539
class FunctionIdentifier(NamedTuple):
640
name: str

0 commit comments

Comments
 (0)