mirror of
https://github.com/ModOrganizer2/pystubs-generation.git
synced 2026-07-27 14:07:13 -07:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
886a50770f | ||
|
|
2ee1505336 | ||
|
|
92dec5b19a | ||
|
|
b33b8bceb1 | ||
|
|
dd80b67a0a | ||
|
|
8789b59f23 | ||
|
|
72c652daaa | ||
|
|
80a365f19a | ||
|
|
1b8d6840e2 | ||
|
|
d7c1ecd6f2 | ||
|
|
6a76dda9d7 | ||
|
|
0c807ad974 | ||
|
|
a3542ca1c4 | ||
|
|
d6760273a2 | ||
|
|
ece93e7cb3 |
@@ -15,15 +15,16 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.12
|
||||
- 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: cp stubs/2.5.0/mobase-stubs/__init__.pyi docs/mobase.py
|
||||
run: |
|
||||
mkdir -p docs/src
|
||||
cp -r stubs/2.5.2/mobase-stubs docs/src/mobase
|
||||
- name: Build
|
||||
run: poetry run sphinx-build -b html docs/source docs/build
|
||||
env:
|
||||
@@ -10,15 +10,11 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.12
|
||||
- uses: abatilo/actions-poetry@v2
|
||||
- name: Install
|
||||
run: |
|
||||
poetry install
|
||||
- name: Lint
|
||||
run: |
|
||||
poetry run black src --check --diff
|
||||
poetry run isort -c src
|
||||
poetry run mypy src
|
||||
poetry run ruff src
|
||||
poetry run pyright src
|
||||
poetry run poe lint
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
replace-with: "$1"
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.12
|
||||
- uses: abatilo/actions-poetry@v2
|
||||
- name: Build
|
||||
run: |
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
poetry version ${TAG#v}
|
||||
poetry build
|
||||
- name: Store the distribution packages
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: stubs/setup/dist/
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
||||
@@ -109,7 +109,6 @@ import mobase
|
||||
import mobase.widgets
|
||||
```
|
||||
|
||||
|
||||
**Note:** Most classes in `mobase` cannot be instantiated, so this is mostly intended
|
||||
for MO2 developers.
|
||||
|
||||
|
||||
+574
-53
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
sphinx-rtd-theme
|
||||
sphinx-autodoc-typehints
|
||||
sphinx-automodapi
|
||||
PyQt6
|
||||
+5
-1
@@ -27,8 +27,9 @@ 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.
|
||||
@@ -39,6 +40,9 @@ 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 -------------------------------------------------
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ This documentation is dedicated to writting MO2 **Python** plugins.
|
||||
plugin-types
|
||||
writing-plugins
|
||||
faq
|
||||
mobase
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
``mobase`` API
|
||||
==============
|
||||
|
||||
.. currentmodule:: mobase
|
||||
|
||||
.. automodapi:: mobase
|
||||
:no-inheritance-diagram:
|
||||
:no-heading:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
@@ -1,7 +0,0 @@
|
||||
mobase
|
||||
======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
mobase
|
||||
@@ -64,10 +64,8 @@ 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
|
||||
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.
|
||||
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>`
|
||||
|
||||
As an example for a game feature take BSA invalidation: If the game requires BSA invalidation it will implement
|
||||
this feature.
|
||||
@@ -147,4 +145,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
+892
File diff suppressed because it is too large
Load Diff
+39
-44
@@ -1,61 +1,56 @@
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "mo2-pystubs-generation"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Holt59 <capelle.mikael@gmail.com>"]
|
||||
license = "MIT"
|
||||
version = "0.1.0"
|
||||
readme = "README.md"
|
||||
packages = [{ include = "mo2", from = "src" }]
|
||||
authors = [{ name = "Holt59", email = "capelle.mikael@gmail.com" }]
|
||||
requires-python = ">=3.12,<4.0"
|
||||
dependencies = [
|
||||
"pyqt6 (==6.11.0)",
|
||||
"pyyaml (>=6.0.3,<7.0.0)",
|
||||
"typing-extensions (>=4.15.0,<5.0.0)",
|
||||
]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
[project.scripts]
|
||||
mo2-stubs-generator = "mo2.stubs.generator.__main__:main"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
pyqt6 = "^6.5.2"
|
||||
pyyaml = "^6.0.1"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^23.9.1"
|
||||
mypy = "^1.5.1"
|
||||
pyright = "^1.1.327"
|
||||
isort = "^5.12.0"
|
||||
ruff = "^0.0.290"
|
||||
flake8 = "^6.1.0"
|
||||
flake8-black = "^0.3.6"
|
||||
flake8-pyproject = "^1.2.3"
|
||||
types-pyyaml = "^6.0.12.11"
|
||||
|
||||
|
||||
[tool.poetry.group.doc.dependencies]
|
||||
sphinx-rtd-theme = "^1.3.0"
|
||||
sphinx-autodoc-typehints = "^1.24.0"
|
||||
sphinx-automodapi = "^0.16.0"
|
||||
sphinx = "^7.2.6"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
requires = ['poetry-core (>=2.0,<3.0)']
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.flake8]
|
||||
max-line-length = 88
|
||||
extend-ignore = ["E203"]
|
||||
[tool.poetry]
|
||||
packages = [{ include = "mo2", from = "src" }]
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
multi_line_output = 3
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pyright = "^1.1.409"
|
||||
ruff = "^0.15.12"
|
||||
types-pyyaml = "^6.0.12.20260508"
|
||||
poethepoet = "^0.45.0"
|
||||
|
||||
[tool.poetry.group.doc.dependencies]
|
||||
sphinx-rtd-theme = "^3.0.2"
|
||||
sphinx-autodoc-typehints = "^3.2.0"
|
||||
sphinx-automodapi = "^0.20.0"
|
||||
sphinx = "^8.2.3"
|
||||
sphinx-autoapi = "^3.6.0"
|
||||
|
||||
[tool.poe.tasks]
|
||||
format-imports = "ruff check --select I src --fix"
|
||||
format-ruff = "ruff format src"
|
||||
format.sequence = ["format-imports", "format-ruff"]
|
||||
lint-ruff = "ruff check src"
|
||||
lint-ruff-format = "ruff format --check src"
|
||||
lint-pyright = "pyright src"
|
||||
lint.sequence = ["lint-ruff", "lint-ruff-format", "lint-pyright"]
|
||||
lint.ignore_fail = "return_non_zero"
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
target-version = "py311"
|
||||
target-version = "py312"
|
||||
|
||||
[tool.mypy]
|
||||
warn_return_any = true
|
||||
warn_unused_configs = true
|
||||
namespace_packages = true
|
||||
[tool.ruff.lint]
|
||||
extend-select = ["B", "Q", "I"]
|
||||
|
||||
[tool.pyright]
|
||||
# reportMissingTypeStubs = true
|
||||
# reportUntypedBaseClass = false
|
||||
typeCheckingMode = "strict"
|
||||
reportMissingTypeStubs = true
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import argparse
|
||||
import inspect
|
||||
import logging
|
||||
import subprocess
|
||||
import types
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
import black
|
||||
import isort
|
||||
|
||||
from .loader import load_mobase
|
||||
from .mtypes import Class, PyTyping
|
||||
from .mtypes import Class, Constant, Enum, Function, PyTyping
|
||||
from .parser import is_enum
|
||||
from .register import MobaseRegister
|
||||
from .utils import Settings, clean_class
|
||||
from .writer import Writer, is_list_of_functions
|
||||
from .writer import Writer, is_list_of
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
|
||||
def extract_objects(module: object, skips: list[str] = []) -> list[tuple[str, type]]:
|
||||
objects: list[tuple[str, type]] = []
|
||||
def extract_objects(
|
||||
module: object, skips: Sequence[str] = []
|
||||
) -> list[tuple[str, object]]:
|
||||
objects: list[tuple[str, object]] = []
|
||||
|
||||
assert hasattr(module, "__name__")
|
||||
module_name: str = module.__name__ # type: ignore
|
||||
@@ -56,12 +57,9 @@ def add_mobase_header(writer: Writer):
|
||||
"Callable",
|
||||
"Dict",
|
||||
"Iterator",
|
||||
"List",
|
||||
"Optional",
|
||||
"overload",
|
||||
"Sequence",
|
||||
"Set",
|
||||
"Tuple",
|
||||
"Type",
|
||||
"TypeVar",
|
||||
"Union",
|
||||
@@ -142,7 +140,7 @@ def main() -> None:
|
||||
}
|
||||
|
||||
# list of objects directly in mobase
|
||||
module_objects: dict[str, list[tuple[str, type]]] = {
|
||||
module_objects: dict[str, list[tuple[str, object]]] = {
|
||||
"mobase": extract_objects(
|
||||
mobase,
|
||||
[
|
||||
@@ -150,7 +148,7 @@ def main() -> None:
|
||||
"IPlugin",
|
||||
],
|
||||
),
|
||||
"mobase.widgets": extract_objects(getattr(mobase, "widgets")),
|
||||
"mobase.widgets": extract_objects(mobase.widgets), # type: ignore
|
||||
}
|
||||
|
||||
for name, objects in module_objects.items():
|
||||
@@ -180,10 +178,10 @@ def main() -> None:
|
||||
# Path the class using the configuration:
|
||||
settings.patch_class(c)
|
||||
|
||||
elif isinstance(c, PyTyping):
|
||||
elif isinstance(c, (PyTyping, Constant)):
|
||||
...
|
||||
|
||||
elif is_list_of_functions(c):
|
||||
elif is_list_of(c, Function):
|
||||
settings.patch_functions(c)
|
||||
|
||||
else:
|
||||
@@ -200,6 +198,21 @@ 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)
|
||||
@@ -210,19 +223,28 @@ def main() -> None:
|
||||
|
||||
module_headers[name](writer)
|
||||
|
||||
for n, o in objects:
|
||||
# Get the corresponding object:
|
||||
c = register.get_object(n)
|
||||
|
||||
for c in stub_objects:
|
||||
writer.print_object(c)
|
||||
|
||||
black.format_file_in_place(
|
||||
output_folder.joinpath("__init__.pyi"),
|
||||
fast=False,
|
||||
mode=black.Mode(is_pyi=True),
|
||||
write_back=black.WriteBack.YES,
|
||||
subprocess.run(
|
||||
[
|
||||
"ruff",
|
||||
"format",
|
||||
"--silent",
|
||||
output_folder.joinpath("__init__.pyi").as_posix(),
|
||||
]
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
"ruff",
|
||||
"check",
|
||||
"--silent",
|
||||
"--select",
|
||||
"I",
|
||||
"--fix",
|
||||
output_folder.joinpath("__init__.pyi").as_posix(),
|
||||
]
|
||||
)
|
||||
isort.api.sort_file(output_folder.joinpath("__init__.pyi"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from typing import Final, TypeVar
|
||||
|
||||
|
||||
@@ -164,7 +165,6 @@ class Argument:
|
||||
|
||||
|
||||
class Exception:
|
||||
|
||||
"""
|
||||
Small class representing exception that can be raised from functions.
|
||||
"""
|
||||
@@ -313,20 +313,20 @@ class Class:
|
||||
self,
|
||||
package: str,
|
||||
name: str,
|
||||
bases: list[Class],
|
||||
methods: list[Method],
|
||||
constants: list[Constant] = [],
|
||||
properties: list[Property] = [],
|
||||
inner_classes: list[Class] = [],
|
||||
bases: Sequence[Class],
|
||||
methods: Sequence[Method],
|
||||
constants: Sequence[Constant] = [],
|
||||
properties: Sequence[Property] = [],
|
||||
inner_classes: Sequence[Class] = [],
|
||||
doc: str = "",
|
||||
):
|
||||
self.package = package
|
||||
self.name = name
|
||||
self.bases = bases
|
||||
self.methods = methods
|
||||
self.properties = properties
|
||||
self.constants = constants
|
||||
self.inner_classes = inner_classes
|
||||
self.bases = list(bases)
|
||||
self.methods = list(methods)
|
||||
self.properties = list(properties)
|
||||
self.constants = list(constants)
|
||||
self.inner_classes = list(inner_classes)
|
||||
self.doc = ""
|
||||
self.abstract = False
|
||||
self.outer_class = None
|
||||
@@ -385,7 +385,6 @@ class Class:
|
||||
|
||||
|
||||
class PyClass(Class):
|
||||
|
||||
"""
|
||||
Class use to wrap Python class to be used as parent class for some classes
|
||||
in mobase.
|
||||
@@ -401,7 +400,6 @@ class PyClass(Class):
|
||||
|
||||
|
||||
class Enum(Class):
|
||||
|
||||
"""
|
||||
Class representing an enum.
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,7 @@ import re
|
||||
import types
|
||||
from collections import OrderedDict, defaultdict
|
||||
from itertools import chain
|
||||
from typing import Any, Iterable, cast
|
||||
from typing import Any, Callable, Iterable, cast
|
||||
|
||||
from .mtypes import (
|
||||
Argument,
|
||||
@@ -118,7 +118,7 @@ def parse_python_signature(s: str, name: str) -> tuple[PyType, list[Argument]]:
|
||||
return PyType(return_type), arguments
|
||||
|
||||
|
||||
def is_enum(e: type) -> bool:
|
||||
def is_enum(e: object) -> bool:
|
||||
"""Check if the given class is an enumeration.
|
||||
|
||||
Args:
|
||||
@@ -134,7 +134,6 @@ def is_enum(e: type) -> bool:
|
||||
|
||||
|
||||
class Overload:
|
||||
|
||||
"""Small class to avoid mypy issues..."""
|
||||
|
||||
return_type: PyType
|
||||
@@ -145,17 +144,18 @@ class Overload:
|
||||
self.arguments = arguments
|
||||
|
||||
|
||||
def parse_pybind11_function_docstring(e: type) -> list[Overload]:
|
||||
def parse_pybind11_function_docstring(name: str, doc: str | None) -> list[Overload]:
|
||||
"""
|
||||
Parse the docstring of the given element.
|
||||
Parse the docstring of a Pybind11 function.
|
||||
|
||||
Args:
|
||||
e: The function to "parse".
|
||||
name: Name of the function.
|
||||
doc: The docstring of the function generated by Pybind11.
|
||||
|
||||
Returns:
|
||||
A list of overloads for the given function.
|
||||
"""
|
||||
lines = (e.__doc__ or "").strip().split("\n")
|
||||
lines = (doc or "").strip().split("\n")
|
||||
|
||||
signatures: list[str]
|
||||
if len(lines) == 1:
|
||||
@@ -163,7 +163,7 @@ def parse_pybind11_function_docstring(e: type) -> list[Overload]:
|
||||
else:
|
||||
signatures = []
|
||||
for line in lines:
|
||||
m = re.match(rf"^[0-9]+[.]\s+({e.__name__}.*)$", line)
|
||||
m = re.match(rf"^[0-9]+[.]\s+({name}.*)$", line)
|
||||
if m:
|
||||
signatures.append(m.group(1).strip())
|
||||
|
||||
@@ -175,16 +175,16 @@ def parse_pybind11_function_docstring(e: type) -> list[Overload]:
|
||||
signature = signature.replace("MOBase::", "mobase.").replace("::", ".")
|
||||
|
||||
try:
|
||||
return_type, arguments = parse_python_signature(signature, e.__name__)
|
||||
except ValueError:
|
||||
raise ValueError(f"invalid signature: {e.__name__}, {e.__doc__}")
|
||||
return_type, arguments = parse_python_signature(signature, name)
|
||||
except ValueError as err:
|
||||
raise ValueError(f"invalid signature: {name}, {doc}") from err
|
||||
overloads.append(Overload(return_type=return_type, arguments=arguments))
|
||||
|
||||
return overloads
|
||||
|
||||
|
||||
def make_functions(e: type) -> list[Function]:
|
||||
overloads = parse_pybind11_function_docstring(e)
|
||||
def make_functions(e: Callable[..., Any]) -> list[Function]:
|
||||
overloads = parse_pybind11_function_docstring(e.__name__, e.__doc__)
|
||||
|
||||
return [
|
||||
Function(
|
||||
@@ -221,7 +221,8 @@ def make_class(e: type, register: MobaseRegister) -> Class:
|
||||
|
||||
# This contains ALL the parent classes, not the direct ones:
|
||||
base_classes: list[Class] = [
|
||||
register.make_object(name) for name in base_classes_s # type: ignore
|
||||
register.make_object(name)
|
||||
for name in base_classes_s # type: ignore
|
||||
]
|
||||
|
||||
# retrieve all the attributes that are not in a base class
|
||||
@@ -295,7 +296,9 @@ def make_class(e: type, register: MobaseRegister) -> Class:
|
||||
|
||||
# otherwise we parse the docstring
|
||||
else:
|
||||
overloads = parse_pybind11_function_docstring(method)
|
||||
overloads = parse_pybind11_function_docstring(
|
||||
method.__name__, method.__doc__
|
||||
)
|
||||
|
||||
for overload in overloads:
|
||||
args = overload.arguments
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from .mtypes import Class, Function, PyTyping
|
||||
from .mtypes import Class, Constant, Function, PyType, PyTyping
|
||||
|
||||
|
||||
class MobaseRegister:
|
||||
@@ -12,20 +12,20 @@ class MobaseRegister:
|
||||
Class that register classes.
|
||||
"""
|
||||
|
||||
objects: dict[str, Class | list[Function] | PyTyping]
|
||||
objects: dict[str, Class | Constant | list[Function] | PyTyping]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.raw_objects: dict[str, type] = OrderedDict()
|
||||
self.raw_objects: dict[str, object] = OrderedDict()
|
||||
self.objects = {}
|
||||
|
||||
def add_object(self, name: str, object: type) -> None:
|
||||
self.raw_objects[name] = object
|
||||
def add_object(self, n: str, o: object, /) -> None:
|
||||
self.raw_objects[n] = o
|
||||
|
||||
def make_object(
|
||||
self, name: str, e: type | None = None
|
||||
) -> Class | list[Function] | PyTyping:
|
||||
self, name: str, e: object | None = None
|
||||
) -> Class | list[Function] | Constant | PyTyping:
|
||||
"""
|
||||
Construct a Function, Class or Enum for the given object.
|
||||
Construct a Function, Class, Constant 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 | list[Function] | PyTyping:
|
||||
def get_object(self, name: str) -> Class | Constant | 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).
|
||||
|
||||
@@ -215,12 +215,14 @@ class Settings:
|
||||
# Check the args:
|
||||
if function_settings.args is not None:
|
||||
if len(function_settings.args) != len(fn.args):
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
f"Mismatch number of arguments for function "
|
||||
f"mobase.{setting_name}."
|
||||
)
|
||||
|
||||
for setting_arg, method_arg in zip(function_settings.args, fn.args):
|
||||
for setting_arg, method_arg in zip(
|
||||
function_settings.args, fn.args, strict=True
|
||||
):
|
||||
method_arg.doc = setting_arg.doc
|
||||
if not setting_arg.type.is_none():
|
||||
method_arg.type = setting_arg.type
|
||||
@@ -241,7 +243,7 @@ class Settings:
|
||||
fn.deprecated = function_settings.deprecated
|
||||
|
||||
else:
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
"Missing settings for function mobase.{}.".format(setting_name)
|
||||
)
|
||||
|
||||
@@ -266,7 +268,7 @@ class Settings:
|
||||
class_settings = self._get_class_settings(cls.canonical_name)
|
||||
|
||||
if class_settings is None:
|
||||
LOGGER.warn("Class {} not found in settings.".format(cls.canonical_name))
|
||||
LOGGER.warning("Class {} not found in settings.".format(cls.canonical_name))
|
||||
return
|
||||
|
||||
if "__doc__" in class_settings and class_settings["__doc__"] is not None:
|
||||
@@ -304,7 +306,7 @@ class Settings:
|
||||
if "type" in settings_property:
|
||||
prop.type = PyType(settings_property["type"])
|
||||
else:
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
"Missing type for property {}.{}.".format(
|
||||
cls.canonical_name, prop.name
|
||||
)
|
||||
@@ -318,7 +320,7 @@ class Settings:
|
||||
prop.doc = settings_property["desc"]
|
||||
|
||||
else:
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
"Missing description for property {}.{}.".format(
|
||||
cls.canonical_name, prop.name
|
||||
)
|
||||
@@ -364,21 +366,21 @@ class Settings:
|
||||
if function_settings.args is not None:
|
||||
method_arguments = m.args if m.is_static() else m.args[1:]
|
||||
if len(function_settings.args) != len(method_arguments):
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
"Mismatch number of arguments for method {}.{}.".format(
|
||||
cls.canonical_name, settings_name
|
||||
)
|
||||
)
|
||||
|
||||
for settings_arg, method_arg in zip(
|
||||
function_settings.args, method_arguments
|
||||
function_settings.args, method_arguments, strict=True
|
||||
):
|
||||
method_arg.doc = settings_arg.doc
|
||||
if (
|
||||
not method_arg.name.startswith("arg")
|
||||
and method_arg.name != settings_arg.name
|
||||
):
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
(
|
||||
"Mismatch argument name for method {}.{}: "
|
||||
"{} {}, using {}."
|
||||
@@ -427,7 +429,7 @@ class Settings:
|
||||
|
||||
if n_overloads > 0 and missing_settings:
|
||||
for settings_name in missing_settings:
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
"Missing settings for method {}.{}.".format(
|
||||
cls.canonical_name, settings_name
|
||||
)
|
||||
@@ -445,7 +447,7 @@ class Settings:
|
||||
# Print items missing in mobase
|
||||
missing_items = [k for k, v in keys.items() if not v]
|
||||
if missing_items:
|
||||
LOGGER.warn(
|
||||
LOGGER.warning(
|
||||
"The following members were found in settings but not in the actual"
|
||||
" class {}: {}.".format(cls.canonical_name, ", ".join(missing_items))
|
||||
)
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import logging
|
||||
from typing import Any, Iterable, TextIO, TypeGuard
|
||||
from typing import Any, TextIO
|
||||
|
||||
from .mtypes import Class, Enum, Function, Method, Property, PyTyping
|
||||
from typing_extensions import TypeIs
|
||||
|
||||
from .mtypes import Class, Constant, Enum, Function, Method, Property, PyTyping
|
||||
from .utils import Settings
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
|
||||
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)
|
||||
def is_list_of_any(e: Any) -> TypeIs[list[Any]]:
|
||||
return isinstance(e, list)
|
||||
|
||||
|
||||
def is_list_of[T](e: Any, t: type[T]) -> TypeIs[list[T]]:
|
||||
return is_list_of_any(e) and all(isinstance(x, t) for x in e)
|
||||
|
||||
|
||||
class Writer:
|
||||
@@ -163,7 +167,7 @@ class Writer:
|
||||
if not prop.is_read_only():
|
||||
self._print("{}@{}.setter".format(indent, prop.name))
|
||||
self._print(
|
||||
"{}def {}(self, arg0: {}): ...".format(
|
||||
"{}def {}(self, arg0: {}) -> None: ...".format(
|
||||
indent, prop.name, self._fix_typing(prop.type.typing())
|
||||
)
|
||||
)
|
||||
@@ -256,13 +260,24 @@ class Writer:
|
||||
def print_typing(self, typ: PyTyping):
|
||||
self._print(f"{typ.name} = {typ.typing}")
|
||||
|
||||
def print_object(self, e: object):
|
||||
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):
|
||||
if isinstance(e, Class):
|
||||
self.print_class(e)
|
||||
|
||||
elif is_list_of_functions(e):
|
||||
elif is_list_of(e, Function):
|
||||
for fn in e:
|
||||
self.print_function(fn)
|
||||
|
||||
elif isinstance(e, PyTyping):
|
||||
self.print_typing(e)
|
||||
|
||||
else:
|
||||
self.print_constent(e)
|
||||
|
||||
@@ -2141,19 +2141,43 @@ class IOrganizer:
|
||||
The (absolute) path to the mods directory.
|
||||
"""
|
||||
...
|
||||
@overload
|
||||
def onAboutToRun(
|
||||
self: IOrganizer, callback: Callable[[str, PyQt6.QtCore.QDir, str], bool]
|
||||
) -> bool:
|
||||
"""
|
||||
Install a new handler to be called when an application is about to run.
|
||||
|
||||
Multiple handlers can be installed. If any of the handler returns `False`, the
|
||||
application will not run.
|
||||
|
||||
Args:
|
||||
callback: The function to call when an application is about to run. The function
|
||||
receives the absolute path to the application to run, the working directory
|
||||
for the run and a string containing the arguments passed to the executable.
|
||||
The function can return False to prevent the application from running.
|
||||
|
||||
Returns:
|
||||
True if the handler was installed properly (there are currently no
|
||||
reasons for this to fail).
|
||||
"""
|
||||
...
|
||||
@overload
|
||||
def onAboutToRun(self: IOrganizer, callback: Callable[[str], bool]) -> bool:
|
||||
"""
|
||||
Install a new handler to be called when an application is about to run.
|
||||
|
||||
Multiple handlers can be installed. If any of the handler returns `False`, the application will
|
||||
not run.
|
||||
Multiple handlers can be installed. If any of the handler returns `False`, the
|
||||
application will not run.
|
||||
|
||||
Args:
|
||||
callback: The function to call when an application is about to run. The parameter is the absolute path
|
||||
to the application to run. The function can return False to prevent the application from running.
|
||||
callback: The function to call when an application is about to run. The parameter
|
||||
is the absolute path to the application to run. The function can return False
|
||||
to prevent the application from running.
|
||||
|
||||
Returns:
|
||||
True if the handler was installed properly (there are currently no reasons for this to fail).
|
||||
True if the handler was installed properly (there are currently no reasons for
|
||||
this to fail).
|
||||
"""
|
||||
...
|
||||
def onFinishedRun(self: IOrganizer, callback: Callable[[str, int], None]) -> bool:
|
||||
@@ -2165,7 +2189,25 @@ class IOrganizer:
|
||||
path to the application, and the second parameter is the exit code of the application.
|
||||
|
||||
Returns:
|
||||
True if the handler was installed properly (there are currently no reasons for this to fail).
|
||||
True if the handler was installed properly (there are currently no reasons for
|
||||
this to fail).
|
||||
"""
|
||||
...
|
||||
def onNextRefresh(
|
||||
self: IOrganizer,
|
||||
callback: Callable[[], None],
|
||||
immediate_if_possible: bool = True,
|
||||
) -> bool:
|
||||
"""
|
||||
Install a new handler to be called on the next refresh or immediately.
|
||||
|
||||
Args:
|
||||
callback: Function to call on the next refresh (or immediately).
|
||||
immediate_if_possible: If True, immediately run the callback if no refresh is currently running.
|
||||
|
||||
Returns:
|
||||
True if the handler was installed properly (there are currently no reasons for
|
||||
this to fail).
|
||||
"""
|
||||
...
|
||||
@overload
|
||||
@@ -2719,14 +2761,12 @@ class IPluginGame(IPlugin):
|
||||
"""
|
||||
|
||||
def __init__(self: IPluginGame) -> None: ...
|
||||
@abc.abstractmethod
|
||||
def CCPlugins(self: IPluginGame) -> Sequence[str]:
|
||||
"""
|
||||
Returns:
|
||||
The current list of active Creation Club plugins.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def DLCPlugins(self: IPluginGame) -> Sequence[str]:
|
||||
"""
|
||||
Returns:
|
||||
@@ -2773,6 +2813,12 @@ class IPluginGame(IPlugin):
|
||||
The directory of the documents folder where configuration files and such for this game reside.
|
||||
"""
|
||||
...
|
||||
def enabledPlugins(self: IPluginGame) -> Sequence[str]:
|
||||
"""
|
||||
Returns:
|
||||
A list of plugins enabled by the game but not in a strict load order.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def executableForcedLoads(
|
||||
self: IPluginGame,
|
||||
@@ -2782,7 +2828,6 @@ class IPluginGame(IPlugin):
|
||||
A list of automatically discovered libraries that can be force loaded with executables.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def executables(self: IPluginGame) -> Sequence[ExecutableInfo]:
|
||||
"""
|
||||
Returns:
|
||||
@@ -2834,7 +2879,6 @@ class IPluginGame(IPlugin):
|
||||
The name of the game (as displayed to the user).
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def gameNexusName(self: IPluginGame) -> str:
|
||||
"""
|
||||
Returns:
|
||||
@@ -2848,7 +2892,6 @@ class IPluginGame(IPlugin):
|
||||
The short name of the game.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def gameVariants(self: IPluginGame) -> Sequence[str]:
|
||||
"""
|
||||
Retrieve the list of variants for this game.
|
||||
@@ -2883,7 +2926,6 @@ class IPluginGame(IPlugin):
|
||||
An URL for the support page of this game.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def iniFiles(self: IPluginGame) -> Sequence[str]:
|
||||
"""
|
||||
Returns:
|
||||
@@ -2928,7 +2970,6 @@ class IPluginGame(IPlugin):
|
||||
The list of game saves in the given folder.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def loadOrderMechanism(self: IPluginGame) -> LoadOrderMechanism:
|
||||
"""
|
||||
Returns:
|
||||
@@ -2958,7 +2999,6 @@ class IPluginGame(IPlugin):
|
||||
The Nexus game ID for this game.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def nexusModOrganizerID(self: IPluginGame) -> int:
|
||||
"""
|
||||
Retrieve the Nexus mod ID of Mod Organizer for this game.
|
||||
@@ -2970,14 +3010,12 @@ class IPluginGame(IPlugin):
|
||||
The Nexus mod ID of Mod Organizer for this game.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def primaryPlugins(self: IPluginGame) -> Sequence[str]:
|
||||
"""
|
||||
Returns:
|
||||
The list of plugins that are part of the game and not considered optional.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def primarySources(self: IPluginGame) -> Sequence[str]:
|
||||
"""
|
||||
Retrieve primary alternative 'short' names for this game.
|
||||
@@ -2996,7 +3034,6 @@ class IPluginGame(IPlugin):
|
||||
The directory where save games are stored.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def secondaryDataDirectories(self: IPluginGame) -> Dict[str, PyQt6.QtCore.QDir]:
|
||||
"""
|
||||
Retrieve the list of secondary data directories. Each directories should be
|
||||
@@ -3032,14 +3069,12 @@ class IPluginGame(IPlugin):
|
||||
variant: The game variant selected by the user.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def sortMechanism(self: IPluginGame) -> SortMechanism:
|
||||
"""
|
||||
Returns:
|
||||
The sort mechanism for this game.
|
||||
"""
|
||||
...
|
||||
@abc.abstractmethod
|
||||
def steamAPPId(self: IPluginGame) -> str:
|
||||
"""
|
||||
Retrieve the Steam app ID for this game.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user