Compare commits

...
11 changed files with 430 additions and 92 deletions
+16
View File
@@ -94,6 +94,22 @@ It is possible to start a (i)python interpreter with `mobase` imported by runnin
python -i -m mo2.stubs.generator.loader ${MO2_INSTALL_PATH} python -i -m mo2.stubs.generator.loader ${MO2_INSTALL_PATH}
``` ```
You can also import `mobase` in your code using the following (after installing
this package):
```python
from mo2.stubs.generator import load_mobase
mobase = load_mobase(MO2_INSTALL_PATH)
# the above will probably not give you type-completion in your IDE or typing, so
# you can use the following (if the stubs are installed)
load_mobase(MO2_INSTALL_PATH)
import mobase
import mobase.widgets
```
**Note:** Most classes in `mobase` cannot be instantiated, so this is mostly intended **Note:** Most classes in `mobase` cannot be instantiated, so this is mostly intended
for MO2 developers. for MO2 developers.
+118 -4
View File
@@ -3004,11 +3004,11 @@ mobase:
__doc__: __doc__:
__abstract__: true __abstract__: true
BinaryName: binaryName:
__doc__: __doc__:
returns: The name of the script extender binary. returns: The name of the script extender binary.
PluginPath: pluginPath:
__doc__: __doc__:
returns: The script extender plugin path, relative to the data folder. returns: The script extender plugin path, relative to the data folder.
@@ -3140,6 +3140,120 @@ mobase:
mobase.widgets: mobase.widgets:
TaskDialog: {} TaskDialog:
__doc__: Customizable choice dialog.
TaskDialogButton: {} __init__:
__doc__: Construct a new TaskDialog.
args:
parent: Parent widget of the dialog.
title: Title of the dialog.
main: Header of the dialog (big text at the top).
content: Main message of the dialog (text below main).
details: Details for the dialog, initially collapsed (bottom of the dialog).
icon: Icon for the dialog.
buttons: List of buttons for the dialog.
remember: Remember the choice for this dialog.
addButton:
__doc__: Add a custom button to this TaskDialog.
args:
button: Button to add to the dialog.
addContent:
__doc__: |
Add a custom widget content to this TaskDialog. Widget content are put between
content and buttons (above buttons).
args:
widget: Widget to add.
exec:
__doc__: |
Display this dialog and wait for user-interaction to return. This is a blocking
function.
returns:
The button clicked by the user. Without custom buttons, this return Ok,
otherwise it returns the button set in the TaskDialogButton.
setContent:
__doc__: Set the top-level message of this dialog.
args:
content: Top-level message to set.
setDetails:
__doc__: |
Set the details for this TaskDialog.
The details are hidden by default and the user can display them by clicking
the "Details" button at the bottom of the TaskDialog.
args:
details: Details content to display. Can be a multi-line string.
setIcon:
__doc__: Set the icon of the dialog.
args:
icon: Icon of the dialog.
setMain:
__doc__: |
Set the main message of the dialog. The main message is displayed at the top of
the dialog in large font.
args:
main: Main message of the dialog.
setRemember:
__doc__: Configure the dialog to remember user-choice.
args:
action:
file:
setTitle:
__doc__: Set the title of the dialog.
args:
title: Title of the dialog.
setWidth:
__doc__: Set the width of the dialog.
args:
width: Width of the dialog.
TaskDialogButton:
__doc__: Special button to be used inside TaskDialog widgets.
__init__.1:
__doc__: Create a TaskDialogButton.
args:
text: Label of the button.
description: Description of the button.
button: Value returned by TaskDialog.exec() if this button is clicked.
__init__.2:
__doc__: Create a TaskDialogButton without description.
args:
text: Label of the button.
button: Value returned by TaskDialog.exec() if this button is clicked.
properties[]:
text:
type: str
desc: Label of the button.
description:
type: str
desc: Description of the button.
button:
type: PyQt6.QtWidgets.QMessageBox.StandardButton
desc: Value returned by TaskDialog.exec() if this button is clicked.
+2
View File
@@ -3,3 +3,5 @@
import logging import logging
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
from .loader import load_mobase # noqa: F401
+22 -25
View File
@@ -3,25 +3,29 @@
import argparse import argparse
import inspect import inspect
import logging import logging
import types
from pathlib import Path from pathlib import Path
from typing import Callable, TextIO, cast from typing import Callable
import black import black
import isort import isort
from . import LOGGER from . import LOGGER
from .loader import load_mobase from .loader import load_mobase
from .mtypes import Class, Function, PyType from .mtypes import Class, PyTyping
from .parser import is_enum from .parser import is_enum
from .register import MobaseRegister from .register import MobaseRegister
from .utils import Settings, clean_class from .utils import Settings, clean_class
from .writer import Writer from .writer import Writer, is_list_of_functions
def extract_objects(module: object, skips: list[str] = []) -> list[tuple[str, object]]: def extract_objects(module: object, skips: list[str] = []) -> list[tuple[str, object]]:
objects: list[tuple[str, object]] = [] objects: list[tuple[str, object]] = []
assert hasattr(module, "__name__")
module_name: str = module.__name__ # type: ignore
for name in dir(module): for name in dir(module):
if name.startswith("__") or name in skips: if name.startswith("__") or name in skips:
continue continue
@@ -32,6 +36,11 @@ def extract_objects(module: object, skips: list[str] = []) -> list[tuple[str, ob
if inspect.ismodule(obj): if inspect.ismodule(obj):
continue continue
# skip imports - type object have wrong __module__?
if hasattr(obj, "__module__") and obj.__module__ != module_name:
if obj.__module__ != types.__name__ or hasattr(types, name):
continue
objects.append((name, obj)) objects.append((name, obj))
return objects return objects
@@ -42,23 +51,22 @@ def add_mobase_header(writer: Writer):
[ [
"abc", "abc",
("enum", ["Enum"]), ("enum", ["Enum"]),
("pathlib", ["Path"]), "os",
( (
"typing", "typing",
[ [
"Callable",
"Dict", "Dict",
"Iterator", "Iterator",
"List", "List",
"Tuple",
"Union",
"Any",
"Optional", "Optional",
"Callable",
"overload", "overload",
"Sequence", "Sequence",
"Set", "Set",
"TypeVar", "Tuple",
"Type", "Type",
"TypeVar",
"Union",
], ],
), ),
"PyQt6.QtCore", "PyQt6.QtCore",
@@ -67,13 +75,6 @@ def add_mobase_header(writer: Writer):
] ]
) )
# Needs to define the MVariant and GameFeatureType type:
writer._print(f"MoVariant = {PyType.MO_VARIANT}")
writer._print(f"FileWrapper = {PyType.FILE_WRAPPER}")
writer._print(f"DirectoryWrapper = {PyType.DIRECTORY_WRAPPER}")
writer._print('GameFeatureType = TypeVar("GameFeatureType")')
writer._print()
def add_mobase_widgets_header(writer: Writer): def add_mobase_widgets_header(writer: Writer):
writer.print_imports( writer.print_imports(
@@ -148,8 +149,6 @@ def main():
"mobase": extract_objects( "mobase": extract_objects(
mobase, mobase,
[ [
# we do not want the real MoVariant
"MoVariant",
# the "real" IPlugin is IPluginBase # the "real" IPlugin is IPluginBase
"IPlugin", "IPlugin",
], ],
@@ -187,7 +186,10 @@ def main():
# Path the class using the configuration: # Path the class using the configuration:
settings.patch_class(c) settings.patch_class(c)
elif isinstance(c, list) and isinstance(c[0], Function): elif isinstance(c, PyTyping):
...
elif is_list_of_functions(c):
settings.patch_functions(c) settings.patch_functions(c)
else: else:
@@ -220,12 +222,7 @@ def main():
# Get the corresponding object: # Get the corresponding object:
c = register.get_object(n) c = register.get_object(n)
if isinstance(c, Class): writer.print_object(c)
writer.print_class(c)
elif isinstance(c, list) and isinstance(c[0], Function):
for fn in c:
writer.print_function(fn)
black.format_file_in_place( black.format_file_in_place(
output_folder.joinpath("__init__.pyi"), output_folder.joinpath("__init__.pyi"),
+5 -2
View File
@@ -2,10 +2,11 @@
import os import os
import sys import sys
from modulefinder import Module
from pathlib import Path from pathlib import Path
def load_mobase(path: Path): def load_mobase(path: os.PathLike) -> Module:
""" """
Load the mobase from the given MO2 installation path and Load the mobase from the given MO2 installation path and
returns it. returns it.
@@ -16,6 +17,8 @@ def load_mobase(path: Path):
Returns: The mobase module. Returns: The mobase module.
""" """
path = Path(path)
# We need absolute path for loading DLL and modules: # We need absolute path for loading DLL and modules:
path = path.resolve() path = path.resolve()
@@ -35,7 +38,7 @@ def load_mobase(path: Path):
import mobase # type: ignore import mobase # type: ignore
return mobase return mobase # type: ignore
if __name__ == "__main__": if __name__ == "__main__":
+27 -9
View File
@@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import re import re
from typing import Final, TypeVar
class PyType: class PyType:
@@ -10,15 +11,6 @@ class PyType:
Class representing a python type. Class representing a python type.
""" """
# The `MoVariant` actual type - This should be list["MoVariant"] and
# Dict[str, "MoVariant"], but mypy (and other type checkers) do not
# handle recursive definition yet:
MO_VARIANT = """Union[None, bool, int, str, list[Any], dict[str, Any]]"""
# File/Directory wrappers
FILE_WRAPPER = """Union[str, PyQt6.QtCore.QFileInfo, Path]"""
DIRECTORY_WRAPPER = """Union[str, PyQt6.QtCore.QDir, Path]"""
name: str name: str
def __init__(self, name: str | type): def __init__(self, name: str | type):
@@ -47,6 +39,9 @@ class PyType:
if self.name == "mobase.IPluginBase": if self.name == "mobase.IPluginBase":
return "IPlugin" return "IPlugin"
# PathLike should be [] in the stubs
self.name = self.name.replace("os.PathLike", "os.PathLike[str]")
return self.name return self.name
def is_none(self) -> bool: def is_none(self) -> bool:
@@ -430,3 +425,26 @@ class Enum(Class):
def is_abstract(self): def is_abstract(self):
return False return False
class PyTyping:
"""
Class representing a typing object, e.g., MoVariant.
"""
name: Final[str]
typing: Final[str]
def __init__(self, name: str, obj: object):
self.name = name
_typing: str
if obj.__module__ == "types":
_typing = str(obj)
# type-var have a weird name, e.g., ~Name
elif type(obj) is TypeVar:
_typing = f'TypeVar("{name}")'
else:
_typing = str(obj)
self.typing = _typing
+5 -1
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from collections import OrderedDict from collections import OrderedDict
from .mtypes import Class, Function from .mtypes import Class, Function, PyTyping
class MobaseRegister: class MobaseRegister:
@@ -46,6 +46,10 @@ class MobaseRegister:
elif callable(e): elif callable(e):
self.objects[name] = make_functions(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)
return self.objects[name] return self.objects[name]
def get_object(self, name: str): def get_object(self, name: str):
+21 -2
View File
@@ -1,12 +1,16 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
from typing import TextIO from typing import TextIO, TypeGuard
from . import LOGGER from . import LOGGER
from .mtypes import Class, Enum, Function, Method, Property from .mtypes import Class, Enum, Function, Method, Property, PyTyping
from .utils import Settings from .utils import Settings
def is_list_of_functions(e: object) -> TypeGuard[list[Function]]:
return isinstance(e, list) and all(isinstance(x, Function) for x in e)
class Writer: class Writer:
_output: TextIO _output: TextIO
@@ -240,3 +244,18 @@ class Writer:
if isinstance(cls, Enum): if isinstance(cls, Enum):
self._print() self._print()
self._print() self._print()
def print_typing(self, typ: PyTyping):
self._print(f"{typ.name} = {typ.typing}")
def print_object(self, e: object):
if isinstance(e, Class):
self.print_class(e)
elif is_list_of_functions(e):
for fn in e:
self.print_function(fn)
elif isinstance(e, PyTyping):
self.print_typing(e)
+63 -32
View File
@@ -3,10 +3,9 @@ from __future__ import annotations
__version__ = "2.5.0" __version__ = "2.5.0"
import abc import abc
import os
from enum import Enum from enum import Enum
from pathlib import Path
from typing import ( from typing import (
Any,
Callable, Callable,
Dict, Dict,
Iterator, Iterator,
@@ -25,12 +24,12 @@ import PyQt6.QtCore
import PyQt6.QtGui import PyQt6.QtGui
import PyQt6.QtWidgets import PyQt6.QtWidgets
MoVariant = Union[None, bool, int, str, list[Any], dict[str, Any]]
FileWrapper = Union[str, PyQt6.QtCore.QFileInfo, Path]
DirectoryWrapper = Union[str, PyQt6.QtCore.QDir, Path]
GameFeatureType = TypeVar("GameFeatureType") GameFeatureType = TypeVar("GameFeatureType")
MoVariant = None | bool | int | str | list[object] | dict[str, object]
def getFileVersion(filepath: FileWrapper) -> str: def getFileVersion(
filepath: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]
) -> str:
""" """
Retrieve the file version of the given executable. Retrieve the file version of the given executable.
@@ -42,7 +41,9 @@ def getFileVersion(filepath: FileWrapper) -> str:
""" """
... ...
def getIconForExecutable(executable: FileWrapper) -> PyQt6.QtGui.QIcon: def getIconForExecutable(
executable: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]
) -> PyQt6.QtGui.QIcon:
""" """
Retrieve the icon of an executable. Currently this always extracts the biggest icon. Retrieve the icon of an executable. Currently this always extracts the biggest icon.
@@ -54,7 +55,9 @@ def getIconForExecutable(executable: FileWrapper) -> PyQt6.QtGui.QIcon:
""" """
... ...
def getProductVersion(executable: FileWrapper) -> str: def getProductVersion(
executable: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]
) -> str:
""" """
Retrieve the product version of the given executable. Retrieve the product version of the given executable.
@@ -343,7 +346,11 @@ class ExecutableForcedLoadSetting:
) -> ExecutableForcedLoadSetting: ... ) -> ExecutableForcedLoadSetting: ...
class ExecutableInfo: class ExecutableInfo:
def __init__(self: ExecutableInfo, title: str, binary: FileWrapper): ... def __init__(
self: ExecutableInfo,
title: str,
binary: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo],
): ...
def arguments(self: ExecutableInfo) -> Sequence[str]: ... def arguments(self: ExecutableInfo) -> Sequence[str]: ...
def asCustom(self: ExecutableInfo) -> ExecutableInfo: ... def asCustom(self: ExecutableInfo) -> ExecutableInfo: ...
def binary(self: ExecutableInfo) -> PyQt6.QtCore.QFileInfo: ... def binary(self: ExecutableInfo) -> PyQt6.QtCore.QFileInfo: ...
@@ -354,7 +361,7 @@ class ExecutableInfo:
def withArgument(self: ExecutableInfo, argument: str) -> ExecutableInfo: ... def withArgument(self: ExecutableInfo, argument: str) -> ExecutableInfo: ...
def withSteamAppId(self: ExecutableInfo, app_id: str) -> ExecutableInfo: ... def withSteamAppId(self: ExecutableInfo, app_id: str) -> ExecutableInfo: ...
def withWorkingDirectory( def withWorkingDirectory(
self: ExecutableInfo, directory: DirectoryWrapper self: ExecutableInfo, directory: Union[str, os.PathLike[str], PyQt6.QtCore.QDir]
) -> ExecutableInfo: ... ) -> ExecutableInfo: ...
def workingDirectory(self: ExecutableInfo) -> PyQt6.QtCore.QDir: ... def workingDirectory(self: ExecutableInfo) -> PyQt6.QtCore.QDir: ...
@@ -1280,7 +1287,7 @@ class IInstallationManager:
def installArchive( def installArchive(
self: IInstallationManager, self: IInstallationManager,
mod_name: GuessedString, mod_name: GuessedString,
archive: FileWrapper, archive: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo],
mod_id: int = 0, mod_id: int = 0,
) -> Tuple[InstallResult, str, int]: ) -> Tuple[InstallResult, str, int]:
""" """
@@ -1945,7 +1952,9 @@ class IOrganizer:
""" """
... ...
def findFileInfos( def findFileInfos(
self: IOrganizer, path: DirectoryWrapper, filter: Callable[[FileInfo], bool] self: IOrganizer,
path: Union[str, os.PathLike[str], PyQt6.QtCore.QDir],
filter: Callable[[FileInfo], bool],
) -> Sequence[FileInfo]: ) -> Sequence[FileInfo]:
""" """
Find files in the virtual directory matching the specified filter. Find files in the virtual directory matching the specified filter.
@@ -1960,7 +1969,9 @@ class IOrganizer:
... ...
@overload @overload
def findFiles( def findFiles(
self: IOrganizer, path: DirectoryWrapper, filter: Callable[[str], bool] self: IOrganizer,
path: Union[str, os.PathLike[str], PyQt6.QtCore.QDir],
filter: Callable[[str], bool],
) -> Sequence[str]: ) -> Sequence[str]:
""" """
Find files in the given folder that matches the given filter. Find files in the given folder that matches the given filter.
@@ -1975,7 +1986,9 @@ class IOrganizer:
... ...
@overload @overload
def findFiles( def findFiles(
self: IOrganizer, path: DirectoryWrapper, patterns: Sequence[str] self: IOrganizer,
path: Union[str, os.PathLike[str], PyQt6.QtCore.QDir],
patterns: Sequence[str],
) -> Sequence[str]: ) -> Sequence[str]:
""" """
Find files in the given folder that matches one of the given glob patterns. Find files in the given folder that matches one of the given glob patterns.
@@ -1990,7 +2003,9 @@ class IOrganizer:
... ...
@overload @overload
def findFiles( def findFiles(
self: IOrganizer, path: DirectoryWrapper, pattern: str self: IOrganizer,
path: Union[str, os.PathLike[str], PyQt6.QtCore.QDir],
pattern: str,
) -> Sequence[str]: ) -> Sequence[str]:
""" """
Find files in the given folder that matches the given glob pattern. Find files in the given folder that matches the given glob pattern.
@@ -2036,7 +2051,9 @@ class IOrganizer:
""" """
... ...
def installMod( def installMod(
self: IOrganizer, filename: FileWrapper, name_suggestion: str = "" self: IOrganizer,
filename: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo],
name_suggestion: str = "",
) -> IModInterface: ) -> IModInterface:
""" """
Install a mod archive at the specified location. Install a mod archive at the specified location.
@@ -2350,7 +2367,9 @@ class IOrganizer:
save_changes: If True, the relevant profile information is saved first (enabled mods and order of mods). save_changes: If True, the relevant profile information is saved first (enabled mods and order of mods).
""" """
... ...
def resolvePath(self: IOrganizer, filename: FileWrapper) -> str: def resolvePath(
self: IOrganizer, filename: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]
) -> str:
""" """
Resolves a path relative to the virtual data directory to its absolute real path. Resolves a path relative to the virtual data directory to its absolute real path.
@@ -2398,9 +2417,9 @@ class IOrganizer:
... ...
def startApplication( def startApplication(
self: IOrganizer, self: IOrganizer,
executable: FileWrapper, executable: Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo],
args: Sequence[str] = [], args: Sequence[str] = [],
cwd: DirectoryWrapper = "", cwd: Union[str, os.PathLike[str], PyQt6.QtCore.QDir] = "",
profile: str = "", profile: str = "",
forcedCustomOverwrite: str = "", forcedCustomOverwrite: str = "",
ignoreCustomOverwrite: bool = False, ignoreCustomOverwrite: bool = False,
@@ -3710,7 +3729,9 @@ class ISaveGame:
""" """
def __init__(self: ISaveGame): ... def __init__(self: ISaveGame): ...
def allFiles(self: ISaveGame) -> Sequence[str]: def allFiles(
self: ISaveGame,
) -> Sequence[Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]]:
""" """
Returns: Returns:
The list of all files related to this save. The list of all files related to this save.
@@ -3727,7 +3748,9 @@ class ISaveGame:
The creation time of the save. The creation time of the save.
""" """
... ...
def getFilepath(self: ISaveGame) -> str: def getFilepath(
self: ISaveGame,
) -> Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]:
""" """
Returns: Returns:
The path name to the (main) file or folder for the save. The path name to the (main) file or folder for the save.
@@ -4235,20 +4258,13 @@ class SaveGameInfo(abc.ABC):
class ScriptExtender(abc.ABC): class ScriptExtender(abc.ABC):
def __init__(self: ScriptExtender): ... def __init__(self: ScriptExtender): ...
@abc.abstractmethod @abc.abstractmethod
def BinaryName(self: ScriptExtender) -> str: def binaryName(self: ScriptExtender) -> str:
""" """
Returns: Returns:
The name of the script extender binary. The name of the script extender binary.
""" """
... ...
@abc.abstractmethod @abc.abstractmethod
def PluginPath(self: ScriptExtender) -> str:
"""
Returns:
The script extender plugin path, relative to the data folder.
"""
...
@abc.abstractmethod
def getArch(self: ScriptExtender) -> int: def getArch(self: ScriptExtender) -> int:
""" """
Returns: Returns:
@@ -4277,13 +4293,24 @@ class ScriptExtender(abc.ABC):
""" """
... ...
@abc.abstractmethod @abc.abstractmethod
def loaderPath(self: ScriptExtender) -> str: def loaderPath(
self: ScriptExtender,
) -> Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]:
""" """
Returns: Returns:
The full path to the script extender loader. The full path to the script extender loader.
""" """
... ...
@abc.abstractmethod @abc.abstractmethod
def pluginPath(
self: ScriptExtender,
) -> Union[str, os.PathLike[str], PyQt6.QtCore.QDir]:
"""
Returns:
The script extender plugin path, relative to the data folder.
"""
...
@abc.abstractmethod
def savegameExtension(self: ScriptExtender) -> str: def savegameExtension(self: ScriptExtender) -> str:
""" """
Retrieve the extension of script extender save files. Retrieve the extension of script extender save files.
@@ -4320,7 +4347,9 @@ class UnmanagedMods(abc.ABC):
""" """
... ...
@abc.abstractmethod @abc.abstractmethod
def referenceFile(self: UnmanagedMods, mod_name: str) -> PyQt6.QtCore.QFileInfo: def referenceFile(
self: UnmanagedMods, mod_name: str
) -> Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]:
""" """
Retrieve the reference file for the requested mod. Retrieve the reference file for the requested mod.
@@ -4335,7 +4364,9 @@ class UnmanagedMods(abc.ABC):
""" """
... ...
@abc.abstractmethod @abc.abstractmethod
def secondaryFiles(self: UnmanagedMods, mod_name: str) -> Sequence[str]: def secondaryFiles(
self: UnmanagedMods, mod_name: str
) -> Sequence[Union[str, os.PathLike[str], PyQt6.QtCore.QFileInfo]]:
""" """
Retrieve the secondary files for the requested mod. Retrieve the secondary files for the requested mod.
+137 -15
View File
@@ -9,6 +9,10 @@ import PyQt6.QtGui
import PyQt6.QtWidgets import PyQt6.QtWidgets
class TaskDialog: class TaskDialog:
"""
Customizable choice dialog.
"""
def __init__( def __init__(
self: TaskDialog, self: TaskDialog,
parent: PyQt6.QtWidgets.QWidget = None, parent: PyQt6.QtWidgets.QWidget = None,
@@ -19,31 +23,149 @@ class TaskDialog:
icon: PyQt6.QtWidgets.QMessageBox.Icon = PyQt6.QtWidgets.QMessageBox.Icon.NoIcon, icon: PyQt6.QtWidgets.QMessageBox.Icon = PyQt6.QtWidgets.QMessageBox.Icon.NoIcon,
buttons: List[TaskDialogButton] = [], buttons: List[TaskDialogButton] = [],
remember: Union[str, Tuple[str, str]] = "", remember: Union[str, Tuple[str, str]] = "",
): ... ):
def addButton(self: TaskDialog, button: TaskDialogButton) -> TaskDialog: ... """
def addContent(self: TaskDialog, widget: PyQt6.QtWidgets.QWidget): ... Construct a new TaskDialog.
def exec(self: TaskDialog) -> PyQt6.QtWidgets.QMessageBox.StandardButton: ...
def setContent(self: TaskDialog, content: str) -> TaskDialog: ... Args:
def setDetails(self: TaskDialog, details: str) -> TaskDialog: ... parent: Parent widget of the dialog.
def setIcon( title: Title of the dialog.
self: TaskDialog, icon: PyQt6.QtWidgets.QMessageBox.Icon main: Header of the dialog (big text at the top).
) -> TaskDialog: ... content: Main message of the dialog (text below main).
def setMain(self: TaskDialog, main: str) -> TaskDialog: ... details: Details for the dialog, initially collapsed (bottom of the dialog).
def setRemember(self: TaskDialog, action: str, file: str = "") -> TaskDialog: ... icon: Icon for the dialog.
def setTitle(self: TaskDialog, title: str) -> TaskDialog: ... buttons: List of buttons for the dialog.
def setWidth(self: TaskDialog, widget: int): ... remember: Remember the choice for this dialog.
"""
...
def addButton(self: TaskDialog, button: TaskDialogButton) -> TaskDialog:
"""
Add a custom button to this TaskDialog.
Args:
button: Button to add to the dialog.
"""
...
def addContent(self: TaskDialog, widget: PyQt6.QtWidgets.QWidget):
"""
Add a custom widget content to this TaskDialog. Widget content are put between
content and buttons (above buttons).
Args:
widget: Widget to add.
"""
...
def exec(self: TaskDialog) -> PyQt6.QtWidgets.QMessageBox.StandardButton:
"""
Display this dialog and wait for user-interaction to return. This is a blocking
function.
Returns:
The button clicked by the user. Without custom buttons, this return Ok, otherwise it returns the button set in the TaskDialogButton.
"""
...
def setContent(self: TaskDialog, content: str) -> TaskDialog:
"""
Set the top-level message of this dialog.
Args:
content: Top-level message to set.
"""
...
def setDetails(self: TaskDialog, details: str) -> TaskDialog:
"""
Set the details for this TaskDialog.
The details are hidden by default and the user can display them by clicking
the "Details" button at the bottom of the TaskDialog.
Args:
details: Details content to display. Can be a multi-line string.
"""
...
def setIcon(self: TaskDialog, icon: PyQt6.QtWidgets.QMessageBox.Icon) -> TaskDialog:
"""
Set the icon of the dialog.
Args:
icon: Icon of the dialog.
"""
...
def setMain(self: TaskDialog, main: str) -> TaskDialog:
"""
Set the main message of the dialog. The main message is displayed at the top of
the dialog in large font.
Args:
main: Main message of the dialog.
"""
...
def setRemember(self: TaskDialog, action: str, file: str = "") -> TaskDialog:
"""
Configure the dialog to remember user-choice.
"""
...
def setTitle(self: TaskDialog, title: str) -> TaskDialog:
"""
Set the title of the dialog.
Args:
title: Title of the dialog.
"""
...
def setWidth(self: TaskDialog, width: int):
"""
Set the width of the dialog.
Args:
width: Width of the dialog.
"""
...
class TaskDialogButton: class TaskDialogButton:
"""
Special button to be used inside TaskDialog widgets.
"""
@property
def button(self) -> PyQt6.QtWidgets.QMessageBox.StandardButton: ...
@button.setter
def button(self, arg0: PyQt6.QtWidgets.QMessageBox.StandardButton): ...
@property
def description(self) -> str: ...
@description.setter
def description(self, arg0: str): ...
@property
def text(self) -> str: ...
@text.setter
def text(self, arg0: str): ...
@overload @overload
def __init__( def __init__(
self: TaskDialogButton, self: TaskDialogButton,
text: str, text: str,
description: str, description: str,
button: PyQt6.QtWidgets.QMessageBox.StandardButton, button: PyQt6.QtWidgets.QMessageBox.StandardButton,
): ... ):
"""
Create a TaskDialogButton.
Args:
text: Label of the button.
description: Description of the button.
button: Value returned by TaskDialog.exec() if this button is clicked.
"""
...
@overload @overload
def __init__( def __init__(
self: TaskDialogButton, self: TaskDialogButton,
text: str, text: str,
button: PyQt6.QtWidgets.QMessageBox.StandardButton, button: PyQt6.QtWidgets.QMessageBox.StandardButton,
): ... ):
"""
Create a TaskDialogButton without description.
Args:
text: Label of the button.
button: Value returned by TaskDialog.exec() if this button is clicked.
"""
...
+14 -2
View File
@@ -11,8 +11,10 @@
import io import io
import os import os
import re import re
from collections import defaultdict
from pathlib import Path
from setuptools import find_packages, setup from setuptools import setup
def read(*names, **kwargs): def read(*names, **kwargs):
@@ -34,18 +36,28 @@ def find_version(*file_paths):
raise RuntimeError("Unable to find version string.") raise RuntimeError("Unable to find version string.")
def find_package_data(path: str):
package_data: dict[str, list[str]] = defaultdict(lambda: [])
for stubfile in Path(path).glob("**/*.pyi"):
package_data[stubfile.parent.as_posix().replace("/", ".")].append(stubfile.name)
return dict(package_data)
long_description = read("README.md") long_description = read("README.md")
package_data = find_package_data("mobase-stubs")
setup( setup(
name="mobase-stubs", name="mobase-stubs",
url="https://github.com/ModOrganizer2/mo2-pystubs-generation", url="https://github.com/ModOrganizer2/mo2-pystubs-generation",
author="Holt59", author="Holt59",
author_email="capelle.mikael@gmail.com",
description="PEP561 stub files for the mobase python API", description="PEP561 stub files for the mobase python API",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
version=find_version("mobase-stubs", "__init__.pyi"), version=find_version("mobase-stubs", "__init__.pyi"),
packages=find_packages(), packages=list(package_data.keys()),
package_data=package_data,
install_requires=[], install_requires=[],
python_requires="==3.10.*", python_requires="==3.10.*",
classifiers=[ classifiers=[