Compare commits

..
27 changed files with 269 additions and 18911 deletions
@@ -15,16 +15,15 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.12
python-version: 3.11
- uses: abatilo/actions-poetry@v2
- name: Install
run: poetry install
run: |
poetry install
- name: Install libgl1
run: sudo apt install -y libgl1 libegl1 libglib2.0-0 libxkbcommon0 libdbus-1-3
- name: Copy stubs
run: |
mkdir -p docs/src
cp -r stubs/2.5.2/mobase-stubs docs/src/mobase
run: cp stubs/2.5.0/mobase-stubs/__init__.pyi docs/mobase.py
- name: Build
run: poetry run sphinx-build -b html docs/source docs/build
env:
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.12
python-version: 3.11
- uses: abatilo/actions-poetry@v2
- name: Install
run: |
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
replace-with: "$1"
- uses: actions/setup-python@v2
with:
python-version: 3.12
python-version: 3.11
- uses: abatilo/actions-poetry@v2
- name: Build
run: |
+1
View File
@@ -109,6 +109,7 @@ import mobase
import mobase.widgets
```
**Note:** Most classes in `mobase` cannot be instantiated, so this is mostly intended
for MO2 developers.
+24 -22
View File
@@ -3,11 +3,12 @@
version: 2
# version of the stubs - this is overridden when publishing
__version__: "2.5.2"
__version__: "2.5.0"
# This is the root of the mobase module and will contain everything
# related to functions / classes, including their documentation.
mobase:
getFileVersion:
__doc__: Retrieve the file version of the given executable.
args:
@@ -301,7 +302,7 @@ mobase:
hasSuffix.2:
__doc__: Check if this entry has the given suffix.
args:
suffix: Suffix to check.
suffix: Suffix to check.
returns: True if this entry is a file and has the given suffix.
isDir:
@@ -486,6 +487,9 @@ mobase:
returns: The number of unregistered features.
GamePlugins:
__abstract__: true
getLoadOrder:
@@ -493,8 +497,8 @@ mobase:
returns:
lightPluginsAreSupported:
returns: True if light plugins are supported, False otherwise.
mediumPluginsAreSupported:
returns: True if medium plugins are supported, False otherwise.
overridePluginsAreSupported:
returns: True if override plugins are supported, False otherwise.
readPluginLists:
__doc__:
args:
@@ -578,6 +582,7 @@ mobase:
returns: The list of variants for this GuessedString.
IDownloadManager:
downloadPath:
__doc__: Retrieve the (absolute) path of the specified download.
args:
@@ -735,7 +740,7 @@ mobase:
given path.
args:
path: Path to the file to create.
replace_if_exists: |
replace_if_exists: |
If True and an entry already exists at the given location, it will be replaced by
a new entry. This will replace both files and directories.
returns: A FileTreeEntry corresponding to the created file.
@@ -998,6 +1003,7 @@ mobase:
returns: The result of the installation.
IModInterface:
absolutePath:
returns: Absolute path to the mod to be used in file system operations.
@@ -1333,6 +1339,7 @@ mobase:
returns: The state of the given mod.
IModRepositoryBridge:
__bases__:
- PyQt6.QtCore.QObject
@@ -2475,6 +2482,7 @@ mobase:
installation did not succeed.
IPluginList:
__doc__: Primary interface to the list of plugins.
isMaster:
@@ -2519,9 +2527,10 @@ mobase:
True if the given file plugin contains no records, False if it does OR if the
file does not exist.
isLightFlagged:
__doc__: |
Determine if a plugin is flagged as light.
Determine if a plugin is flagged as light
In gamebryo games, a master file will usually have a .esl file extension but
technically an esp can be flagged as light.
@@ -2531,16 +2540,16 @@ mobase:
True if the given plugin is a light plugin, False otherwise or if the
file does not exist.
isMediumFlagged:
isOverlayFlagged:
__doc__: |
Determine if a plugin is flagged as medium.
Determine if a plugin is flagged as overlay
This plugin flag was added in Starfield and signifies plugin records that
update existing records
args:
name: Filename of the plugin (without path but with file extension).
returns: |
True if the given plugin is a medium plugin, False otherwise or if the
True if the given plugin is an overlay plugin, False otherwise or if the
file does not exist.
hasLightExtension:
@@ -2653,6 +2662,7 @@ mobase:
returns: The state of the plugin.
IPluginModPage:
_parentWidget:
abstract: false
returns: The parent widget.
@@ -2698,27 +2708,15 @@ mobase:
by qt are implemented (including dds) but no audio files and no 3d mesh formats.
genFilePreview:
__doc__: Generate a preview widget for the specified file.
__doc__: Generate a preview for the specified file.
args:
filename: Path to the file to preview.
max_size: Maximum size of the generated widget.
returns: The widget showing a preview of the file.
genDataPreview:
__doc__: Generate a preview widget from in-memory data.
args:
file_data: In-memory data to preview.
filename: Name of the file the data comes from.
max_size: Maximum size of the generated widget.
returns: The widget showing a preview of the in-memory data.
supportedExtensions:
returns: The list of file extensions that are supported by this preview plugin.
supportsArchives:
__doc__: Check if this preview plugin supports preview from in-memory data.
returns: True if the plugin supports preview from raw data, False otherwise.
IPluginRequirement:
__doc__: Class representing requirements for plugins.
@@ -2801,6 +2799,7 @@ mobase:
plugin: The name of the plugin that must be enabled.
returns: The constructed requirement.
IPluginTool:
__doc__: |
This is the simplest of plugin interfaces. Such plugins simply place an icon inside the tools sub-menu
@@ -2928,6 +2927,7 @@ mobase:
returns:
Mapping:
__init__.1:
__doc__: Creates an empty Mapping.
@@ -3348,6 +3348,7 @@ mobase:
returns: The version scheme in effect for this VersionInfo.
mobase.widgets:
TaskDialog:
__doc__: Customizable choice dialog.
@@ -3436,6 +3437,7 @@ mobase.widgets:
args:
width: Width of the dialog.
TaskDialogButton:
__doc__: Special button to be used inside TaskDialog widgets.
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
sphinx-rtd-theme
sphinx-autodoc-typehints
sphinx-automodapi
PyQt6
+1 -5
View File
@@ -27,9 +27,8 @@ extensions = [
"sphinx.ext.autodoc",
"sphinx_autodoc_typehints",
"sphinx.ext.napoleon",
"autoapi.extension",
# "sphinx.ext.autosummary",
# "sphinx_automodapi.automodapi",
"sphinx_automodapi.automodapi",
]
# Add any paths that contain templates here, relative to this directory.
@@ -40,9 +39,6 @@ templates_path = ["_templates"]
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
autoapi_dirs = ["../src"]
autoapi_member_order = "groupwise"
# -- Options for HTML output -------------------------------------------------
+1
View File
@@ -18,6 +18,7 @@ This documentation is dedicated to writting MO2 **Python** plugins.
plugin-types
writing-plugins
faq
mobase
+11
View File
@@ -0,0 +1,11 @@
``mobase`` API
==============
.. currentmodule:: mobase
.. automodapi:: mobase
:no-inheritance-diagram:
:no-heading:
.. toctree::
:maxdepth: 4
+7
View File
@@ -0,0 +1,7 @@
mobase
======
.. toctree::
:maxdepth: 4
mobase
+5 -3
View File
@@ -64,8 +64,10 @@ These plugins (shall eventually) implement all the game specific features and fu
able to add support for further games.
The plugin is also responsible to help MO determine if (and where) the game is installed in the first place.
Since supporting a game properly requires extensions in many places of the UI.
To allow this without creating one huge plugin interface that involves every aspect of MO,
game plugins can register only the features they need to MO2 using :meth:`registerFeature<mobase.IGameFeatures.registerFeature>`
To allow this without creating one huge plugin interface that involves every aspect of MO, game plugins
expose a *feature list*.
The list of possible features can be found in the "game_features" project and each feature can itself be
considered a plugin interface.
As an example for a game feature take BSA invalidation: If the game requires BSA invalidation it will implement
this feature.
@@ -145,4 +147,4 @@ File Mappings
This interface allows plugins to add virtual file (or directory) links to the virtual file system in addition to the
mod files.
Profile-local save games, ini-files and load-orders are all implemented this way in MO2.
Profile-local save games, ini-files and load-orders are all implemented this way in MO2.
Generated
+172 -221
View File
File diff suppressed because it is too large Load Diff
+11 -10
View File
@@ -12,22 +12,20 @@ mo2-stubs-generator = "mo2.stubs.generator.__main__:main"
[tool.poetry.dependencies]
python = "^3.12"
pyqt6 = "6.7.1"
pyqt6 = "^6.7.0"
pyyaml = "^6.0.1"
typing-extensions = "^4.12.2"
[tool.poetry.group.dev.dependencies]
pyright = "^1.1.374"
ruff = "^0.5.5"
types-pyyaml = "^6.0.12.20240724"
poethepoet = "^0.27.0"
pyright = "^1.1.365"
ruff = "^0.4.7"
types-pyyaml = "^6.0.12.20240311"
poethepoet = "^0.26.1"
[tool.poetry.group.doc.dependencies]
sphinx-rtd-theme = "^2.0.0"
sphinx-autodoc-typehints = "^2.2.3"
sphinx-automodapi = "^0.17.0"
sphinx = "<8"
sphinx-autoapi = "^3.2.1"
sphinx-autodoc-typehints = "^2.1.0"
sphinx-automodapi = "^0.16.0"
sphinx = "^7.3.7"
[build-system]
requires = ["poetry-core"]
@@ -49,6 +47,9 @@ target-version = "py310"
[tool.ruff.lint]
extend-select = ["B", "Q", "I"]
[tool.ruff.lint.isort]
known-first-party = ['tas']
[tool.pyright]
typeCheckingMode = "strict"
reportMissingTypeStubs = true
+8 -25
View File
@@ -8,7 +8,7 @@ from pathlib import Path
from typing import Callable
from .loader import load_mobase
from .mtypes import Class, Constant, Enum, Function, PyTyping
from .mtypes import Class, PyTyping
from .parser import is_enum
from .register import MobaseRegister
from .utils import Settings, clean_class
@@ -146,8 +146,6 @@ def main() -> None:
[
# the "real" IPlugin is IPluginBase
"IPlugin",
# this is deprecated
"PluginSetting",
],
),
"mobase.widgets": extract_objects(mobase.widgets), # type: ignore
@@ -173,9 +171,6 @@ def main() -> None:
# Create the corresponding object:
c = register.make_object(n, o)
for n, _o in objects:
c = register.get_object(n)
if isinstance(c, Class):
# Clean the class (e.g., remove duplicates methods due to wrappers):
clean_class(c)
@@ -183,7 +178,7 @@ def main() -> None:
# Path the class using the configuration:
settings.patch_class(c)
elif isinstance(c, (PyTyping, Constant)):
elif isinstance(c, PyTyping):
...
elif is_list_of_functions(c):
@@ -203,21 +198,6 @@ def main() -> None:
# create directory if required
output_folder.mkdir(parents=True, exist_ok=True)
# sort the stubs
def _key_fn(o: Class | Constant | list[Function] | PyTyping) -> tuple[int, ...]:
# order is PyTyping -> Constant -> Function -> Enum -> Top-Level Class -> Child Level Classes
return (
not isinstance(o, PyTyping),
not isinstance(o, Constant),
not isinstance(o, list),
not isinstance(o, Enum),
isinstance(o, Class) and len(o.all_bases),
)
stub_objects = sorted(
(register.get_object(n) for n, _o in objects), key=_key_fn
)
# write everything
with open(output_folder.joinpath("__init__.pyi"), "w") as output:
writer = Writer(package=name, output=output, settings=settings)
@@ -228,22 +208,25 @@ def main() -> None:
module_headers[name](writer)
for c in stub_objects:
for n, _o in objects:
# Get the corresponding object:
c = register.get_object(n)
writer.print_object(c)
subprocess.run(
[
"ruff",
"format",
"--silent",
"format",
output_folder.joinpath("__init__.pyi").as_posix(),
]
)
subprocess.run(
[
"ruff",
"check",
"--silent",
"check",
"--select",
"I",
"--fix",
+1 -1
View File
@@ -33,7 +33,7 @@ def load_mobase(path: os.PathLike[Any]) -> Module:
os.add_dll_directory(str(path.joinpath("dlls"))) # type: ignore
# We need to add plugins/data to sys.path, mainly for PyQt6
sys.path.insert(1, path.joinpath("proxies", "python", "libs").as_posix())
sys.path.insert(1, path.joinpath("plugins", "plugin_python", "libs").as_posix())
import mobase # type: ignore
+1 -3
View File
@@ -234,8 +234,6 @@ def make_class(e: type, register: MobaseRegister) -> Class:
# members to exclude
EXCLUDED_MEMBERS = [
"__init_subclass__",
"__dict__",
"__doc__",
"__module__",
"__subclasshook__",
"__hash__",
@@ -348,7 +346,7 @@ def make_class(e: type, register: MobaseRegister) -> Class:
direct_bases: list[Class] = []
for c in e.__bases__:
if c.__module__ not in ("pybind11_builtins", "builtins"):
if c.__module__ != "pybind11_builtins":
b = register.get_object(c.__name__)
assert isinstance(b, Class)
direct_bases.append(b)
+7 -7
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from collections import OrderedDict
from .mtypes import Class, Constant, Function, PyType, PyTyping
from .mtypes import Class, Function, PyTyping
class MobaseRegister:
@@ -12,7 +12,7 @@ class MobaseRegister:
Class that register classes.
"""
objects: dict[str, Class | Constant | list[Function] | PyTyping]
objects: dict[str, Class | list[Function] | PyTyping]
def __init__(self) -> None:
self.raw_objects: dict[str, type] = OrderedDict()
@@ -23,9 +23,9 @@ class MobaseRegister:
def make_object(
self, name: str, e: type | None = None
) -> Class | list[Function] | Constant | PyTyping:
) -> Class | list[Function] | PyTyping:
"""
Construct a Function, Class, Constant or Enum for the given object.
Construct a Function, Class or Enum for the given object.
Args:
name: The name of the object to inspect.
@@ -47,14 +47,14 @@ class MobaseRegister:
self.objects[name] = make_class(e, self)
elif callable(e):
self.objects[name] = make_functions(e)
# typing stuff
elif type(e).__module__ == "types" or type(e).__module__ == "typing":
self.objects[name] = PyTyping(name, e)
else:
self.objects[name] = Constant(name, type=PyType(type(e)), value=e)
return self.objects[name]
def get_object(self, name: str) -> Class | Constant | list[Function] | PyTyping:
def get_object(self, name: str) -> Class | list[Function] | PyTyping:
"""
Retrieve the object if the given name. Fails if no object with this
name exists (if `make_object(name, ...)` has never been called).
+4 -17
View File
@@ -1,15 +1,13 @@
import logging
from typing import Any, Iterable, TextIO
from typing import Any, Iterable, TextIO, TypeGuard
from typing_extensions import TypeIs
from .mtypes import Class, Constant, Enum, Function, Method, Property, PyTyping
from .mtypes import Class, Enum, Function, Method, Property, PyTyping
from .utils import Settings
LOGGER = logging.getLogger(__package__)
def is_list_of_functions(e: Any | Iterable[Any]) -> TypeIs[list[Function]]:
def is_list_of_functions(e: Any | Iterable[Any]) -> TypeGuard[list[Function]]:
if not isinstance(e, list):
return False
return all(isinstance(x, Function) for x in e)
@@ -258,15 +256,7 @@ class Writer:
def print_typing(self, typ: PyTyping):
self._print(f"{typ.name} = {typ.typing}")
def print_constent(self, constant: Constant):
assert constant.type is not None
self._print(
"{}: {} = ...".format(
constant.name, self._fix_typing(constant.type.typing())
)
)
def print_object(self, e: Class | Constant | list[Function] | PyTyping):
def print_object(self, e: object):
if isinstance(e, Class):
self.print_class(e)
@@ -276,6 +266,3 @@ class Writer:
elif isinstance(e, PyTyping):
self.print_typing(e)
else:
self.print_constent(e)
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More