Compare commits

...
19 Commits
Author SHA1 Message Date
Mikaël Capelle 72c652daaa Fix stubs generator. 2024-08-09 10:01:38 +02:00
Mikaël Capelle 80a365f19a Fix CI for 2.5.2. 2024-08-09 10:01:27 +02:00
Mikaël Capelle 1b8d6840e2 Prepare stubs for 2.5.2. 2024-08-01 14:20:18 +02:00
Mikaël Capelle d7c1ecd6f2 Prepare stubs for 2.5.1. 2024-06-02 11:52:58 +02:00
Mikaël Capelle 6a76dda9d7 Update poetry.lock for stubs. 2024-06-02 11:52:58 +02:00
Mikaël Capelle 0c807ad974 Update everything to target Python 3.12. 2024-06-02 11:52:58 +02:00
Mikaël Capelle a3542ca1c4 Update following uibase change for game features. 2024-06-02 11:52:55 +02:00
Mikaël Capelle d6760273a2 Switch to ruff for linting and formatting. 2024-06-02 11:52:13 +02:00
Mikaël Capelle ece93e7cb3 Update for MO2 2.5. 2023-10-04 19:27:07 +02:00
Mikaël Capelle 4fa50f89a4 Fix missing optional annotation in some cases. 2023-09-23 13:21:45 +02:00
Mikaël Capelle 962a3c2c1f Minor cleaning for Python 3.11. 2023-09-23 12:23:18 +02:00
Mikaël Capelle 7c2581eb0c Merge pull request #3 from ModOrganizer2/dev/pypi-trusted-publisher
Switch to PyPi trusted publisher.
2023-09-20 19:41:03 +02:00
Mikaël Capelle 336d80a5c6 Try to switch to PyPi trusted publisher. 2023-09-20 19:38:47 +02:00
Mikaël Capelle a1f5ef3a45 Merge pull request #2 from ModOrganizer2/dev/fix-deploy
Fix deployment of documentation.
2023-09-20 19:07:42 +02:00
Mikaël Capelle be6a6761f7 Fix deployment of documentation. 2023-09-20 19:07:28 +02:00
Mikaël Capelle 52f835dc75 Merge pull request #1 from ModOrganizer2/qt6
Qt6, PyBind11, new linters...
2023-09-19 22:28:39 +02:00
Mikaël Capelle 9ee5222bbc Fix documentation. 2023-09-19 22:27:05 +02:00
Mikaël Capelle 4655e1f3eb Fix typing error on CI. 2023-09-19 21:41:11 +02:00
Mikaël Capelle c4a6e2266d Many fixes and update for MO2 2.5. 2023-09-19 21:30:57 +02:00
33 changed files with 11094 additions and 629 deletions
+26 -24
View File
@@ -1,36 +1,38 @@
name: Build Documentation
on:
pull_request:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
persist-credentials: false
# Standard drop-in approach that should work for most people.
- uses: ammaraskar/sphinx-action@master
env:
PYTHONPATH: .
with:
pre-build-command: "apt-get update -y && apt-get install -y libgl1-mesa-glx && cp stubs/2.4.0/mobase.pyi docs/mobase.py"
docs-folder: "docs/"
- name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.4.1
with:
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
SSH: true
REPOSITORY_NAME: ModOrganizer2/python-plugins-doc
BRANCH: master
FOLDER: docs/build/html
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.12
- uses: abatilo/actions-poetry@v2
- name: 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.2/mobase-stubs/__init__.pyi docs/mobase.py
- name: Build
run: poetry run sphinx-build -b html docs/source docs/build
env:
PYTHONPATH: docs
- if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
name: Deploy Documentation
uses: JamesIves/github-pages-deploy-action@v4
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
repository-name: ModOrganizer2/python-plugins-doc
branch: master
folder: docs/build
+12 -16
View File
@@ -5,20 +5,16 @@ on: [push, pull_request]
jobs:
checks:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox
- name: Test with tox
run: tox -e py38-lint
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.12
- uses: abatilo/actions-poetry@v2
- name: Install
run: |
poetry install
- name: Lint
run: |
poetry run poe lint
-18
View File
@@ -1,18 +0,0 @@
name: Check Documentation
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
# Standard drop-in approach that should work for most people.
- uses: ammaraskar/sphinx-action@master
env:
PYTHONPATH: .
with:
pre-build-command: "apt-get update -y && apt-get install -y libgl1-mesa-glx && cp stubs/2.4.0/mobase.pyi docs/mobase.py"
docs-folder: "docs/"
+45 -29
View File
@@ -1,41 +1,57 @@
# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: Upload Python Package
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
on:
push:
tags: ["*"]
jobs:
deploy:
build:
name: Build distribution 📦
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Replace string
uses: frabert/replace-string-action@v1.1
id: version
with:
string: ${{ github.ref_name }}
pattern: "v?([0-9][.][0-9][.][0-9]).*"
replace-with: "$1"
- uses: actions/setup-python@v2
with:
python-version: 3.12
- uses: abatilo/actions-poetry@v2
- name: Build
run: |
cd stubs/setup
mkdir mobase-stubs
cp -r ../${{ steps.version.outputs.replaced }}/mobase-stubs/* mobase-stubs/
sed -i 's/__version__ = ".*"/__version__ = "${{ github.ref_name }}"/' mobase-stubs/__init__.pyi
TAG=${{ github.ref_name }}
poetry version ${TAG#v}
poetry build
- name: Store the distribution packages
uses: actions/upload-artifact@v3
with:
name: python-package-distributions
path: stubs/setup/dist/
publish-to-pypi:
name: Publish Python 🐍 distribution 📦 to PyPI
needs:
- build
runs-on: ubuntu-latest
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
- uses: actions/checkout@v2
- name: Replace string
uses: frabert/replace-string-action@v1.1
id: version
with:
string: ${{ github.ref_name }}
pattern: "v?([0-9][.][0-9][.][0-9]).*"
replace-with: "$1"
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
cd stubs/setup
cp -r ../${{ steps.version.outputs.replaced }}/mobase-stubs/* mobase-stubs/
sed -i 's/__version__ = ".*"/__version__ = "${{ github.ref_name }}"/' mobase-stubs/__init__.pyi
python setup.py sdist bdist_wheel
twine upload dist/*
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
+3 -3
View File
@@ -16,7 +16,7 @@ MO2.
You can install stubs for a specific version of MO2:
```bash
pip install mobase-stubs==2.3.2.*
pip install mobase-stubs==2.5.*
```
Some words of warning:
@@ -38,8 +38,8 @@ have a `python310.dll` in your MO2 installation path, then you need **Python 3.1
To generate the stubs, you can run:
```bash
# install the package (-e if you want editable mode)
pip install [-e] .
# install the package
poetry install
# change the output folder to whatever you want
mo2-stubs-generator -c configs/config-2.4.yml -o mobase-stubs ${MO2_INSTALL_PATH}
+263 -56
View File
File diff suppressed because it is too large Load Diff
-20
View File
@@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-35
View File
@@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
+1 -1
View File
@@ -1,4 +1,4 @@
sphinx-rtd-theme
sphinx-autodoc-typehints
sphinx-automodapi
PyQt5
PyQt6
+2 -10
View File
@@ -10,21 +10,13 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath("../../stubs/2.4.0/"))
# -- Project information -----------------------------------------------------
project = "MO2 Python Plugin API"
copyright = "2020, Holt59"
copyright = "2023, Holt59"
author = "Holt59"
# The full version, including alpha/beta/rc tags
release = "2.3rc1"
# -- General configuration ---------------------------------------------------
@@ -69,5 +61,5 @@ html_favicon = "mo2.ico"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# html_static_path = ["_static"]
html_extra_path = [".nojekyll"]
Generated
+787
View File
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
[tool.poetry]
name = "mo2-pystubs-generation"
version = "0.1.0"
description = ""
authors = ["Holt59 <capelle.mikael@gmail.com>"]
license = "MIT"
readme = "README.md"
packages = [{ include = "mo2", from = "src" }]
[tool.poetry.scripts]
mo2-stubs-generator = "mo2.stubs.generator.__main__:main"
[tool.poetry.dependencies]
python = "^3.12"
pyqt6 = "6.7.1"
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"
[tool.poetry.group.doc.dependencies]
sphinx-rtd-theme = "^2.0.0"
sphinx-autodoc-typehints = "^2.2.3"
sphinx-automodapi = "^0.17.0"
sphinx = "<8"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[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]
target-version = "py310"
[tool.ruff.lint]
extend-select = ["B", "Q", "I"]
[tool.pyright]
typeCheckingMode = "strict"
reportMissingTypeStubs = true
-40
View File
@@ -1,40 +0,0 @@
[flake8]
# Use black line length:
max-line-length = 88
extend-ignore =
# See https://github.com/PyCQA/pycodestyle/issues/373
E203,
per-file-ignores =
*.pyi: E301, E302, E305, E501, E701, E741, F401, F403, F405, F822
# Since typing.pyi defines "overload" this is not recognized by flake8 as typing.overload.
# Unfortunately, flake8 does not allow to "noqa" just a specific error inside the file itself.
typing.pyi: E301, E302, E305, E501, E701, E741, F401, F403, F405, F811, F822
[mypy]
warn_return_any = True
warn_unused_configs = True
namespace_packages = True
[isort]
profile = black
multi_line_output = 3
[tox:tox]
skipsdist = true
envlist = py310-lint
[testenv:py310-lint]
skip_install = true
deps =
black
mypy
flake8
flake8-black
git+https://github.com/TilmanK/PyQt6-stubs.git
types-PyYAML
isort
commands =
black src --check --diff
flake8 src
mypy src
isort -c src
-35
View File
@@ -1,35 +0,0 @@
# -*- encoding: utf-8 -*-
from setuptools import find_namespace_packages, setup
install_requires = ["black", "isort"]
dev_requires = [
"black",
"flake8-black",
"flake8",
"types-chardet",
]
setup(
name="mo2-stubs-generator",
version="1.0.0",
package_dir={"": "src"},
packages=find_namespace_packages(where="src", include=["mo2.*"]),
author="Holt59",
author_email="capelle.mikael@gmail",
description="Python stubs generator for mobase (MO2 Python API).",
long_description=open("README.md").read(),
url="https://github.com/ModOrganizer2/pystubs-generation",
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
],
license="MIT",
install_requires=install_requires,
extras_require={"dev": dev_requires},
entry_points={
"console_scripts": ["mo2-stubs-generator=mo2.stubs.generator.__main__:main"],
},
)
+3 -3
View File
@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
import logging
from .loader import load_mobase
LOGGER = logging.getLogger(__name__)
from .loader import load_mobase # noqa: F401
__all__ = ["load_mobase", "LOGGER"]
+31 -29
View File
@@ -1,27 +1,26 @@
# -*- encoding: utf-8 -*-
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 . import LOGGER
from .loader import load_mobase
from .mtypes import Class, PyTyping
from .mtypes import Class, Constant, PyTyping
from .parser import is_enum
from .register import MobaseRegister
from .utils import Settings, clean_class
from .writer import Writer, is_list_of_functions
LOGGER = logging.getLogger(__package__)
def extract_objects(module: object, skips: list[str] = []) -> list[tuple[str, object]]:
objects: list[tuple[str, object]] = []
def extract_objects(
module: object, skips: Sequence[str] = []
) -> list[tuple[str, type]]:
objects: list[tuple[str, type]] = []
assert hasattr(module, "__name__")
module_name: str = module.__name__ # type: ignore
@@ -58,12 +57,9 @@ def add_mobase_header(writer: Writer):
"Callable",
"Dict",
"Iterator",
"List",
"Optional",
"overload",
"Sequence",
"Set",
"Tuple",
"Type",
"TypeVar",
"Union",
@@ -90,8 +86,7 @@ def add_mobase_widgets_header(writer: Writer):
)
def main():
def main() -> None:
parser = argparse.ArgumentParser("stubs generator for the MO2 python interface")
parser.add_argument(
"install_dir",
@@ -145,7 +140,7 @@ def main():
}
# list of objects directly in mobase
module_objects: dict[str, list[tuple[str, object]]] = {
module_objects: dict[str, list[tuple[str, type]]] = {
"mobase": extract_objects(
mobase,
[
@@ -153,11 +148,10 @@ def main():
"IPlugin",
],
),
"mobase.widgets": extract_objects(mobase.widgets),
"mobase.widgets": extract_objects(mobase.widgets), # type: ignore
}
for name, objects in module_objects.items():
# load settings from the configuration
settings: Settings = Settings(register)
if config_path is not None:
@@ -174,19 +168,17 @@ def main():
# Process everything:
for n, o in objects:
# Create the corresponding object:
c = register.make_object(n, o)
if isinstance(c, Class):
# Clean the class (e.g., remove duplicates methods due to wrappers):
clean_class(c)
# 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):
@@ -208,7 +200,6 @@ def main():
# write everything
with open(output_folder.joinpath("__init__.pyi"), "w") as output:
writer = Writer(package=name, output=output, settings=settings)
# the __future__ import must be at the beginning
@@ -217,20 +208,31 @@ def main():
module_headers[name](writer)
for n, o in objects:
for n, _o in objects:
# Get the corresponding object:
c = register.get_object(n)
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__":
+4 -6
View File
@@ -1,12 +1,11 @@
# -*- encoding: utf-8 -*-
import os
import sys
from modulefinder import Module
from pathlib import Path
from typing import Any
def load_mobase(path: os.PathLike) -> Module:
def load_mobase(path: os.PathLike[Any]) -> Module:
"""
Load the mobase from the given MO2 installation path and
returns it.
@@ -30,8 +29,8 @@ def load_mobase(path: os.PathLike) -> Module:
[str(path), str(path.joinpath("dlls")), os.environ.get("PATH", "")]
)
else:
os.add_dll_directory(str(path))
os.add_dll_directory(str(path.joinpath("dlls")))
os.add_dll_directory(str(path)) # type: ignore
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("plugins", "plugin_python", "libs").as_posix())
@@ -42,7 +41,6 @@ def load_mobase(path: os.PathLike) -> Module:
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
+15 -20
View File
@@ -1,8 +1,7 @@
# -*- encoding: utf-8 -*-
from __future__ import annotations
import re
from collections.abc import Sequence
from typing import Final, TypeVar
@@ -122,7 +121,6 @@ class Argument:
@property
def value(self) -> str | None:
value = self._value
if value is None:
@@ -167,7 +165,6 @@ class Argument:
class Exception:
"""
Small class representing exception that can be raised from functions.
"""
@@ -316,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
@@ -341,7 +338,7 @@ class Class:
for ic in self.inner_classes:
ic.outer_class = self
def is_abstract(self):
def is_abstract(self) -> bool:
"""
Returns:
True if this class is abstract, False otherwise.
@@ -349,7 +346,7 @@ class Class:
return self.abstract or any(bc.is_abstract() for bc in self.bases)
@property
def canonical_name(self):
def canonical_name(self) -> str:
"""
Returns:
The canonical name of this class.
@@ -357,13 +354,13 @@ class Class:
name = self.name
oc = self.outer_class
while oc is not None:
name = "{}.{}".format(oc.name, name)
name = f"{oc.name}.{name}"
oc = oc.outer_class
return name
@property
def full_name(self):
def full_name(self) -> str:
"""
Returns:
The full name of this class, i.e., package.canonical_name.
@@ -388,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.
@@ -404,7 +400,6 @@ class PyClass(Class):
class Enum(Class):
"""
Class representing an enum.
"""
+33 -25
View File
@@ -1,13 +1,11 @@
# -*- encoding: utf-8 -*-
import inspect
import logging
import re
import types
from collections import OrderedDict, defaultdict
from itertools import chain
from typing import Iterable, cast
from typing import Any, Iterable, cast
from . import LOGGER
from .mtypes import (
Argument,
Class,
@@ -22,8 +20,12 @@ from .mtypes import (
)
from .register import MobaseRegister
LOGGER = logging.getLogger(__package__)
def magic_split(value: str, sep=",", open="(<[", close=")>]"):
def magic_split(
value: str, sep: str = ",", open: str = "(<[", close: str = ")>]"
) -> list[str]:
"""
Split the value according to the given separator, but keeps together elements
within the given separator. Useful to split C++ signature function since type names
@@ -42,8 +44,8 @@ def magic_split(value: str, sep=",", open="(<[", close=")>]"):
Returns: The list of split parts from value.
"""
i, j = 0, 0
s: list[str] = []
r = []
s: list[int] = []
r: list[str] = []
while i < len(value):
j = i + 1
while j < len(value):
@@ -96,8 +98,8 @@ def parse_python_signature(s: str, name: str) -> tuple[PyType, list[Argument]]:
args = magic_split(m.group(1).strip(), ",", open="[", close="]")
return_type = m.group(2)
arguments = []
for i, pa in enumerate(args):
arguments: list[Argument] = []
for pa in args:
m = re.search(
r"(?P<name>[^:]+)\s*:\s*(?P<type>[^=]+)\s*(=\s*(?P<value>[^,]+))?",
pa.strip(),
@@ -106,9 +108,12 @@ def parse_python_signature(s: str, name: str) -> tuple[PyType, list[Argument]]:
raise ValueError(f"invalid argument: {pa}, {s}")
matches = m.groupdict()
arguments.append(
Argument(matches["name"], PyType(matches["type"]), matches["value"])
)
type_ = matches["type"]
if matches["value"] == "None":
if "None" not in type_ and "MoVariant" not in type_:
type_ = type_ + " | None"
arguments.append(Argument(matches["name"], PyType(type_), matches["value"]))
return PyType(return_type), arguments
@@ -129,7 +134,6 @@ def is_enum(e: type) -> bool:
class Overload:
"""Small class to avoid mypy issues..."""
return_type: PyType
@@ -140,7 +144,7 @@ class Overload:
self.arguments = arguments
def parse_pybind11_function_docstring(e) -> list[Overload]:
def parse_pybind11_function_docstring(e: type) -> list[Overload]:
"""
Parse the docstring of the given element.
@@ -150,7 +154,7 @@ def parse_pybind11_function_docstring(e) -> list[Overload]:
Returns:
A list of overloads for the given function.
"""
lines = e.__doc__.strip().split("\n")
lines = (e.__doc__ or "").strip().split("\n")
signatures: list[str]
if len(lines) == 1:
@@ -166,20 +170,19 @@ def parse_pybind11_function_docstring(e) -> list[Overload]:
# them...
overloads: list[Overload] = []
for signature in signatures:
# fix MOBase:: in some places to get proper Python types
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__}")
except ValueError as err:
raise ValueError(f"invalid signature: {e.__name__}, {e.__doc__}") from err
overloads.append(Overload(return_type=return_type, arguments=arguments))
return overloads
def make_functions(e) -> list[Function]:
def make_functions(e: type) -> list[Function]:
overloads = parse_pybind11_function_docstring(e)
return [
@@ -217,7 +220,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
@@ -310,7 +314,9 @@ def make_class(e: type, register: MobaseRegister) -> Class:
for base_class in base_classes:
for biclass in base_class.inner_classes:
if isinstance(biclass, Enum) and biclass.name == base_name:
arg._value = base_class.name + "." + value
arg._value = ( # pyright: ignore[reportPrivateUsage]
base_class.name + "." + value
)
methods.append(
Method(
@@ -323,8 +329,8 @@ def make_class(e: type, register: MobaseRegister) -> Class:
)
# Retrieve the attributes:
constants = []
properties = []
constants: list[Constant] = []
properties: list[Property] = []
for name, attr in all_attrs:
if callable(attr) or isinstance(attr, type):
continue
@@ -341,7 +347,9 @@ def make_class(e: type, register: MobaseRegister) -> Class:
direct_bases: list[Class] = []
for c in e.__bases__:
if c.__module__ != "pybind11_builtins":
direct_bases.append(register.get_object(c.__name__))
b = register.get_object(c.__name__)
assert isinstance(b, Class)
direct_bases.append(b)
# Forcing QWidget base for XWidget classes since these do not show up
# and we use a trick:
@@ -356,7 +364,7 @@ def make_class(e: type, register: MobaseRegister) -> Class:
# check if it an enum
if is_enum(e):
# all pybind11 enums have a .__entries attribute
values = e.__entries # type: ignore
values = cast(dict[str, tuple[int, Any]], e.__entries) # type: ignore
# drop the __init__
methods = [m for m in methods if m.name != "__init__"]
+11 -9
View File
@@ -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,18 +12,20 @@ class MobaseRegister:
Class that register classes.
"""
objects: dict[str, Class | list[Function]]
objects: dict[str, Class | Constant | list[Function] | PyTyping]
def __init__(self):
def __init__(self) -> None:
self.raw_objects: dict[str, type] = OrderedDict()
self.objects = {}
def add_object(self, name, object):
def add_object(self, name: str, object: type) -> None:
self.raw_objects[name] = object
def make_object(self, name: str, e: type | None = None) -> Class | list[Function]:
def make_object(
self, name: str, e: type | 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.
@@ -45,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):
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).

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