mirror of
https://github.com/ModOrganizer2/pystubs-generation.git
synced 2026-07-27 14:07:13 -07:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33b7222b13 | ||
|
|
e083e0832e | ||
|
|
478af17c58 | ||
|
|
6ca51655aa | ||
|
|
0ec04f4e9e | ||
|
|
d43d067c84 | ||
|
|
06e0fe1cca | ||
|
|
c57280578d | ||
|
|
178b1d111d |
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
python-version: '3.10'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
@@ -37,4 +37,4 @@ jobs:
|
||||
cd stubs/setup
|
||||
cp ../${{ steps.version.outputs.replaced }}/mobase.pyi mobase-stubs/__init__.pyi
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
twine upload dist/*
|
||||
|
||||
@@ -642,7 +642,7 @@ mobase:
|
||||
The path to copy the entry to. If the path ends with / or \\, the entry will
|
||||
be copied in the corresponding directory instead of replacing it. If the
|
||||
given path is empty (`""`), the entry is copied directly under this tree.
|
||||
policy: Policy to use to resolve conflicts.
|
||||
insert_policy: Policy to use to resolve conflicts.
|
||||
returns: The new entry (copy of the specified entry).
|
||||
raises:
|
||||
RuntimeError: If the entry could not be copied.
|
||||
@@ -2947,7 +2947,7 @@ mobase:
|
||||
getMissingAssets:
|
||||
__doc__: Retrieve missing assets from the save.
|
||||
args:
|
||||
filepath: The save to find missing assets for.
|
||||
save: The save to find missing assets for.
|
||||
returns: |
|
||||
A collection of missing assets and the modules that can supply those assets.
|
||||
|
||||
@@ -3095,7 +3095,7 @@ mobase:
|
||||
args:
|
||||
value: String to parse.
|
||||
scheme: Scheme to use to parse the string.
|
||||
manual_input: True if the given string should be treated as user input.
|
||||
is_manual: True if the given string should be treated as user input.
|
||||
|
||||
scheme:
|
||||
returns: The version scheme in effect for this VersionInfo.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+22
-23
@@ -15,7 +15,7 @@ This guide assumes that:
|
||||
`Python extension <https://marketplace.visualstudio.com/items?itemName=ms-python.python>`_.
|
||||
- You have Python installed: https://www.python.org/downloads/.
|
||||
|
||||
- It is recommended but not mandatory to use the Python version that is used by MO2.
|
||||
- You must use the Python version that used by MO2.
|
||||
You can check the ``pythonXX.dll`` in the MO2 installation folder to find the Python version used by MO2 (``python38.dll`` means Python 3.8).
|
||||
- You obviously need a valid MO2 installation: https://github.com/modorganizer2/modorganizer/releases
|
||||
|
||||
@@ -28,8 +28,8 @@ Preparation
|
||||
-----------
|
||||
|
||||
**Note:** This part is optional but highly recommended if you want a proper environment to work with.
|
||||
Everything here is written to be as simple as possible but you can of course adapt it to your preferences: use a python virtual
|
||||
environment, use workspace settings instead of global ones, etc.
|
||||
Everything here is written to be as simple as possible but you can of course adapt it to your preferences:
|
||||
use a python virtual environment, use workspace settings instead of global ones, etc.
|
||||
|
||||
1. Get the ``mobase`` stubs
|
||||
...........................
|
||||
@@ -39,11 +39,14 @@ as ``flake8`` or ``mypy``.
|
||||
Instead, we provide `stubs <https://stackoverflow.com/questions/59051631/what-is-the-use-of-stub-files-pyi-in-python>`_
|
||||
which can be used for auto-completion or type-checking.
|
||||
|
||||
The stubs for ``mobase`` are available at https://github.com/ModOrganizer2/pystubs-generation/tree/master/stubs.
|
||||
You want to download the ``mobase.pyi`` file in the folder corresponding to your MO2 version and put it under ``$MO2DIR/plugins/data``.
|
||||
You can install the stubs for ``mobase`` using ``pip``:
|
||||
|
||||
.. code::
|
||||
|
||||
pip install mobase-stubs
|
||||
|
||||
This will install the stubs for ``mobase`` but also for PyQt5, which is heavily used by MO2.
|
||||
|
||||
**Note:** It is possible to put the stubs in a different location, but we are going to use ``$MO2DIR/plugins/data`` for PyQt5,
|
||||
so we might as well use it for the stubs.
|
||||
|
||||
2. Configure Visual Studio Code for ``mobase``
|
||||
..............................................
|
||||
@@ -57,32 +60,28 @@ Open ``settings.json`` (Ctrl+Shift+P, then "Open Settings (JSON)"), and add the
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.mypyEnabled": true,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.autoComplete.extraPaths": [
|
||||
"$MO2DIR\\plugins\\data",
|
||||
]
|
||||
|
||||
3. Configure ``mypy`` to find the ``mobase`` stubs
|
||||
..................................................
|
||||
3. [Optional] Configure ``black`` to auto-format your source files
|
||||
..................................................................
|
||||
|
||||
There are multiply way to configure ``mypy``:
|
||||
This step is optional for your own plugin but recent MO2 plugins use ``black``
|
||||
to get consistent formatting.
|
||||
|
||||
1. You can create a ``mypy.ini`` file somewhere containing:
|
||||
You can install ``black`` with ``pip``:
|
||||
|
||||
.. code-block:: ini
|
||||
.. code::
|
||||
|
||||
[mypy]
|
||||
mypy_path = $MO2DIR\plugins\data
|
||||
pip install black flake8-black
|
||||
|
||||
And then add the following to ``settings.json`` (with the correct path):
|
||||
To configure Visual Studio Code to auto-format your code with ``black`` when saving, open ``settings.json``
|
||||
(Ctrl+Shift+P, then "Open Settings (JSON)"), and add the following entries:
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"python.linting.mypyArgs": [
|
||||
"--config-file=path-to-mypy.ini",
|
||||
]
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"python.formatting.provider": "black",
|
||||
|
||||
2. You can set the ``MYPYPATH`` environment variable to ``$MO2DIR\plugins\data`` (this requires
|
||||
restarting VS code).
|
||||
|
||||
4. [Optional] Automatically reload plugins during development
|
||||
.............................................................
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
logging.basicConfig(stream=sys.stderr, format="%(levelname)s: %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
+2
-2
@@ -4,7 +4,6 @@ import importlib.machinery
|
||||
import importlib.util
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -16,6 +15,7 @@ def load_module(name: str, path: Path):
|
||||
|
||||
# Extract the spec:
|
||||
spec = importlib.util.spec_from_loader(name, loader)
|
||||
assert spec is not None
|
||||
|
||||
# Create the module and execute it?
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
@@ -54,7 +54,7 @@ def load_mobase(path: Path, moprivate: bool = False):
|
||||
os.add_dll_directory(str(path)) # type: ignore[attr-defined]
|
||||
os.add_dll_directory(str(path.joinpath("dlls"))) # type: ignore[attr-defined]
|
||||
|
||||
# We need to add plugins/data to sys.path, mainly for PyQt5
|
||||
# We need to add plugins/data to sys.path, mainly for PyQt6
|
||||
sys.path.insert(1, path.joinpath("plugins", "data").as_posix())
|
||||
|
||||
mobase = load_module("mobase", path.joinpath("plugins", "data", "pythonrunner.dll"))
|
||||
|
||||
+7
-8
@@ -1,9 +1,8 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from typing import Optional, List, Any, Dict, Union
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from . import logger
|
||||
from . import utils
|
||||
from . import logger, utils
|
||||
|
||||
|
||||
class Type:
|
||||
@@ -20,7 +19,7 @@ class Type:
|
||||
|
||||
def __init__(self, name: Union[str, type]):
|
||||
# Import only here since we change the path to find them:
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
if isinstance(name, type):
|
||||
name = name.__name__
|
||||
@@ -120,9 +119,9 @@ class CType(Type):
|
||||
REPLACEMENTS = {
|
||||
"QString": "str",
|
||||
"QStringList": "List[str]",
|
||||
"QWidget *": "PyQt5.QtWidgets.QWidget",
|
||||
"QMainWindow *": "PyQt5.QtWidgets.QMainWindow",
|
||||
"QObject *": "PyQt5.QtCore.QObject",
|
||||
"QWidget *": "PyQt6.QtWidgets.QWidget",
|
||||
"QMainWindow *": "PyQt6.QtWidgets.QMainWindow",
|
||||
"QObject *": "PyQt6.QtCore.QObject",
|
||||
"void *": "object",
|
||||
"api::object": "object",
|
||||
}
|
||||
@@ -141,7 +140,7 @@ class CType(Type):
|
||||
|
||||
def _try_fix(self, name, settings: utils.Settings):
|
||||
|
||||
from .parser import parse_ctype, magic_split, parse_csig
|
||||
from .parser import magic_split, parse_csig, parse_ctype
|
||||
from .register import MOBASE_REGISTER
|
||||
|
||||
pname = name
|
||||
|
||||
+21
-23
@@ -2,26 +2,24 @@
|
||||
|
||||
import inspect
|
||||
import re
|
||||
from collections import OrderedDict, defaultdict
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
from collections import defaultdict, OrderedDict
|
||||
|
||||
from typing import List, Tuple, Optional, Dict, Union
|
||||
|
||||
from .register import MobaseRegister
|
||||
from .mtypes import (
|
||||
Type,
|
||||
CType,
|
||||
Class,
|
||||
PyClass,
|
||||
Enum,
|
||||
Arg,
|
||||
Ret,
|
||||
Method,
|
||||
Constant,
|
||||
Property,
|
||||
Function,
|
||||
)
|
||||
from . import logger
|
||||
from .mtypes import (
|
||||
Arg,
|
||||
Class,
|
||||
Constant,
|
||||
CType,
|
||||
Enum,
|
||||
Function,
|
||||
Method,
|
||||
Property,
|
||||
PyClass,
|
||||
Ret,
|
||||
Type,
|
||||
)
|
||||
from .register import MobaseRegister
|
||||
|
||||
|
||||
def magic_split(value: str, sep=",", open="(<", close=")>"):
|
||||
@@ -206,9 +204,9 @@ def parse_psig(s: str, name: str) -> Tuple[Type, List[Arg]]:
|
||||
n: str = pa.strip()
|
||||
d: Optional[str] = None
|
||||
if pa.find("=") != -1:
|
||||
n, d = pa.split("=")
|
||||
n, ds = pa.split("=")
|
||||
n = n.strip()
|
||||
d = d.strip()
|
||||
d = ds.strip()
|
||||
elif i > len(args) - c - 1:
|
||||
d = Arg.DEFAULT_NONE
|
||||
pargs.append(Arg(n, Type(t), d))
|
||||
@@ -320,7 +318,7 @@ def make_enum(fullname: str, e: type) -> Enum:
|
||||
|
||||
class Overload:
|
||||
|
||||
""" Small class to avoid mypy issues... """
|
||||
"""Small class to avoid mypy issues..."""
|
||||
|
||||
rtype: Type
|
||||
args: List[Arg]
|
||||
@@ -585,10 +583,10 @@ def make_class(fullname: str, e: type, register: MobaseRegister) -> Class:
|
||||
if e.__name__.endswith("Widget"):
|
||||
logger.info(
|
||||
"Forcing base {} for class {}.".format(
|
||||
"PyQt5.QtWidgets.QWidget", e.__name__
|
||||
"PyQt6.QtWidgets.QWidget", e.__name__
|
||||
)
|
||||
)
|
||||
direct_bases.append(PyClass("PyQt5.QtWidgets.QWidget"))
|
||||
direct_bases.append(PyClass("PyQt6.QtWidgets.QWidget"))
|
||||
|
||||
return Class(
|
||||
e.__name__,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from collections import OrderedDict
|
||||
from typing import Optional, Dict, Union, List
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from . import logger
|
||||
from .mtypes import Class, Type, CType, Function
|
||||
from .mtypes import Class, CType, Function, Type
|
||||
|
||||
|
||||
class MobaseRegister:
|
||||
@@ -37,7 +37,7 @@ class MobaseRegister:
|
||||
Returns:
|
||||
A Class object for the given type, or a list of function overloads.
|
||||
"""
|
||||
from .parser import make_enum, make_class, is_enum, make_functions
|
||||
from .parser import is_enum, make_class, make_enum, make_functions
|
||||
|
||||
if e is None:
|
||||
e = self.raw_objects[name]
|
||||
|
||||
+17
-4
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections import OrderedDict, defaultdict
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
List,
|
||||
@@ -11,14 +12,12 @@ from typing import (
|
||||
TextIO,
|
||||
Tuple,
|
||||
Union,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from . import logger
|
||||
from . import mtypes
|
||||
|
||||
import yaml
|
||||
|
||||
from . import logger, mtypes
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .register import MobaseRegister
|
||||
|
||||
@@ -339,6 +338,20 @@ class Settings:
|
||||
|
||||
for sarg, marg in zip(fsettings.args, margs):
|
||||
marg.doc = sarg.doc
|
||||
if (
|
||||
not marg.name.startswith("arg")
|
||||
and marg.name != sarg.name
|
||||
):
|
||||
logger.warn(
|
||||
"Mismatch argument name for method {}.{}: {} {}, using {}.".format( # noqa: E501
|
||||
cls.canonical_name,
|
||||
sname,
|
||||
marg.name,
|
||||
sarg.name,
|
||||
sarg.name,
|
||||
)
|
||||
)
|
||||
|
||||
marg.name = sarg.name
|
||||
if not sarg.type.is_none():
|
||||
marg.type = sarg.type
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from typing import TextIO, List, Union, Tuple
|
||||
from typing import List, TextIO, Tuple, Union
|
||||
|
||||
from . import logger
|
||||
from .mtypes import Function, Class, Method, Property, Enum
|
||||
from .mtypes import Class, Enum, Function, Method, Property
|
||||
from .utils import Settings
|
||||
|
||||
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import black
|
||||
|
||||
from generator import logger
|
||||
from generator.loader import load_mobase
|
||||
from generator.register import MOBASE_REGISTER
|
||||
from generator.mtypes import Class, Function, Type
|
||||
from generator.parser import is_enum
|
||||
from generator.mtypes import Type, Class, Function
|
||||
from generator.register import MOBASE_REGISTER
|
||||
from generator.utils import Settings, clean_class
|
||||
from generator.writer import Writer
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser("Stubs generator for the MO2 python interface")
|
||||
parser.add_argument(
|
||||
"install_dir",
|
||||
@@ -136,9 +134,9 @@ with open(args.output, "w") as output:
|
||||
"Type",
|
||||
],
|
||||
),
|
||||
"PyQt5.QtCore",
|
||||
"PyQt5.QtGui",
|
||||
"PyQt5.QtWidgets",
|
||||
"PyQt6.QtCore",
|
||||
"PyQt6.QtGui",
|
||||
"PyQt6.QtWidgets",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -15,19 +15,26 @@ warn_return_any = True
|
||||
warn_unused_configs = True
|
||||
namespace_packages = True
|
||||
|
||||
[isort]
|
||||
profile = black
|
||||
multi_line_output = 3
|
||||
|
||||
[tox:tox]
|
||||
skipsdist = true
|
||||
envlist = py38-lint
|
||||
envlist = py310-lint
|
||||
|
||||
[testenv:py38-lint]
|
||||
[testenv:py310-lint]
|
||||
skip_install = true
|
||||
deps =
|
||||
black
|
||||
mypy
|
||||
flake8
|
||||
flake8-black
|
||||
PyQt5-stubs
|
||||
git+https://github.com/TilmanK/PyQt6-stubs.git
|
||||
types-PyYAML
|
||||
isort
|
||||
commands =
|
||||
black generator main.py --check --diff
|
||||
flake8 generator main.py
|
||||
mypy generator main.py
|
||||
isort -c generator main.py
|
||||
|
||||
@@ -795,7 +795,7 @@ class IFileTree(FileTreeEntry):
|
||||
self,
|
||||
entry: "FileTreeEntry",
|
||||
path: str = "",
|
||||
policy: "IFileTree.InsertPolicy" = InsertPolicy.FAIL_IF_EXISTS,
|
||||
insert_policy: "IFileTree.InsertPolicy" = InsertPolicy.FAIL_IF_EXISTS,
|
||||
) -> "FileTreeEntry":
|
||||
"""
|
||||
Move the given entry to the given path under this tree.
|
||||
@@ -816,7 +816,7 @@ class IFileTree(FileTreeEntry):
|
||||
path: The path to copy the entry to. If the path ends with / or \\, the entry will
|
||||
be copied in the corresponding directory instead of replacing it. If the
|
||||
given path is empty (`""`), the entry is copied directly under this tree.
|
||||
policy: Policy to use to resolve conflicts.
|
||||
insert_policy: Policy to use to resolve conflicts.
|
||||
|
||||
Returns:
|
||||
The new entry (copy of the specified entry).
|
||||
@@ -3900,12 +3900,12 @@ class SaveGameInfo(abc.ABC):
|
||||
|
||||
def __init__(self): ...
|
||||
@abc.abstractmethod
|
||||
def getMissingAssets(self, filepath: "ISaveGame") -> Dict[str, List[str]]:
|
||||
def getMissingAssets(self, save: "ISaveGame") -> Dict[str, List[str]]:
|
||||
"""
|
||||
Retrieve missing assets from the save.
|
||||
|
||||
Args:
|
||||
filepath: The save to find missing assets for.
|
||||
save: The save to find missing assets for.
|
||||
|
||||
Returns:
|
||||
A collection of missing assets and the modules that can supply those assets.
|
||||
@@ -4155,7 +4155,7 @@ class VersionInfo:
|
||||
self,
|
||||
value: str,
|
||||
scheme: "VersionScheme" = VersionScheme.DISCOVER,
|
||||
manual_input: bool = False,
|
||||
is_manual: bool = False,
|
||||
):
|
||||
"""
|
||||
Update this VersionInfo by parsing the given string using the given scheme.
|
||||
@@ -4163,7 +4163,7 @@ class VersionInfo:
|
||||
Args:
|
||||
value: String to parse.
|
||||
scheme: Scheme to use to parse the string.
|
||||
manual_input: True if the given string should be treated as user input.
|
||||
is_manual: True if the given string should be treated as user input.
|
||||
"""
|
||||
...
|
||||
def scheme(self) -> "VersionScheme":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
@@ -46,8 +47,8 @@ setup(
|
||||
version=find_version("mobase-stubs", "__init__.pyi"),
|
||||
package_data={"mobase-stubs": ["*.pyi"]},
|
||||
packages=["mobase-stubs"],
|
||||
install_requires=["PyQt5-stubs==5.14.2"],
|
||||
python_requires="==3.8.*",
|
||||
install_requires=[],
|
||||
python_requires="==3.10.*",
|
||||
classifiers=[
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
|
||||
Reference in New Issue
Block a user