mirror of
https://github.com/ModOrganizer2/pystubs-generation.git
synced 2026-07-27 14:07:13 -07:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e9919fb94 | ||
|
|
422e29417c | ||
|
|
ece93e7cb3 | ||
|
|
4fa50f89a4 | ||
|
|
962a3c2c1f | ||
|
|
7c2581eb0c | ||
|
|
336d80a5c6 | ||
|
|
a1f5ef3a45 | ||
|
|
be6a6761f7 | ||
|
|
52f835dc75 | ||
|
|
9ee5222bbc | ||
|
|
4655e1f3eb | ||
|
|
c4a6e2266d | ||
|
|
966a84a59a | ||
|
|
3a5c16af21 | ||
|
|
3eb1daa053 | ||
|
|
0eca0f7644 | ||
|
|
7467a2b11c | ||
|
|
eb420fcd79 | ||
|
|
156b827389 | ||
|
|
104e8612d8 | ||
|
|
4f24d363ec |
@@ -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.11
|
||||
- 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.0/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
|
||||
|
||||
@@ -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.11
|
||||
- uses: abatilo/actions-poetry@v2
|
||||
- name: Install
|
||||
run: |
|
||||
poetry install
|
||||
- name: Lint
|
||||
run: |
|
||||
poetry run poe lint
|
||||
|
||||
@@ -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/"
|
||||
@@ -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: ["*.dev[0-9]+"]
|
||||
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.11
|
||||
- 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 ../${{ steps.version.outputs.replaced }}/mobase.pyi mobase-stubs/__init__.pyi
|
||||
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
|
||||
|
||||
+2
-1
@@ -2,9 +2,10 @@
|
||||
.mypy_cache
|
||||
__pycache__
|
||||
.vscode
|
||||
**/*.egg-info
|
||||
|
||||
# The 'bin/' directory:
|
||||
bin
|
||||
docs/build
|
||||
docs/mobase.py
|
||||
docs/source/api
|
||||
docs/source/api
|
||||
|
||||
@@ -16,23 +16,12 @@ MO2.
|
||||
You can install stubs for a specific version of MO2:
|
||||
|
||||
```bash
|
||||
pip install mobase-stubs==2.3.2.*
|
||||
```
|
||||
|
||||
If you want development stubs, you can install them this way:
|
||||
```bash
|
||||
# Clone this repository:
|
||||
git clone https://github.com/ModOrganizer2/pystubs-generation.git
|
||||
|
||||
# Install the stubs:
|
||||
cd pystubs-generation/stubs/setup
|
||||
pip install .
|
||||
pip install mobase-stubs==2.5.*
|
||||
```
|
||||
|
||||
Some words of warning:
|
||||
|
||||
- The stubs are as correct as possible, but some errors are expected.
|
||||
- If you see a `InterfaceNotImplemented` class anywhere in the stubs, it means that
|
||||
a proper interface is currently not available.
|
||||
- Some classes are said (in the stubs) to inherit `QWidget` or `QObject`. This is true
|
||||
on the C++ side but NOT on the python side. The inheritance is only added to help with
|
||||
auto-completion since these classes also override `__getattr__` to dispatch to the
|
||||
@@ -44,35 +33,39 @@ Some words of warning:
|
||||
|
||||
The stubs are generated using python by parsing the `mobase` module.
|
||||
You need the version of python that matches your current MO2 installation: e.g., if you
|
||||
have a `python38.dll` in your MO2 installation path, then you need **Python 3.8**.
|
||||
have a `python310.dll` in your MO2 installation path, then you need **Python 3.10**.
|
||||
|
||||
To generate the stubs, you can run:
|
||||
|
||||
```
|
||||
# Change the output folder to whatever you want:
|
||||
python main.py -c configs\config-2.4.yml ${MO2_INSTALL_PATH}
|
||||
```bash
|
||||
# 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}
|
||||
```
|
||||
|
||||
Where `${MO2_INSTALL_PATH}` is the path to your MO2 installation (the one containing `ModOrganizer.exe`).
|
||||
Where `${MO2_INSTALL_PATH}` is the path to your MO2 installation (the one
|
||||
containing `ModOrganizer.exe`).
|
||||
|
||||
The stubs are generated under `stubs/setup/mobase-stubs/__init__.pyi`, you
|
||||
can change the output file by using the `-o` option
|
||||
The latest stubs are kept under `stubs/setup/mobase-stubs/__init__.pyi`,
|
||||
and when a new version is released, the stubs are backed-up under
|
||||
`stubs/x.y.z/mobase.pyi`.
|
||||
The stubs are generated under `stubs/setup/mobase-stubs` by default, you
|
||||
can change the output file by using the `-o` option.
|
||||
The stubs under `stubs/setup/mobase-stubs` should not be committed as these are
|
||||
generated from the version stubs under `stubs/${VERSION}/mobase-stubs`.
|
||||
|
||||
A few options are available for `main.py`:
|
||||
A few options are available for `mo2-stubs-generator`:
|
||||
|
||||
```
|
||||
usage: Stubs generator for the MO2 python interface [-h] [-o OUTPUT] [-v] [-c CONFIG] INSTALL_DIR
|
||||
```bash
|
||||
$ mo2-stubs-generator --help
|
||||
usage: stubs generator for the MO2 python interface [-h] [-o OUTPUT] [-v] [-c CONFIG] INSTALL_DIR
|
||||
|
||||
positional arguments:
|
||||
INSTALL_DIR installation directory of Mod Organizer 2
|
||||
|
||||
optional arguments:
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-o OUTPUT, --output OUTPUT
|
||||
output file (default stubs/setup/mobase-stubs/__init__.pyi)
|
||||
output folder (default stubs/setup/mobase-stubs)
|
||||
-v, --verbose verbose mode (all logs go to stderr)
|
||||
-c CONFIG, --config CONFIG
|
||||
configuration file
|
||||
@@ -81,19 +74,7 @@ optional arguments:
|
||||
The stubs generator will try hard to find a valid stubs for all classes
|
||||
and methods of `mobase`.
|
||||
A lot of information is available through the `-v` options. Without it,
|
||||
only conversions or fixes
|
||||
considered "strange" will be shown.
|
||||
For instance, here is the output with the current `config-2.4.yml` file:
|
||||
|
||||
```
|
||||
WARNING: Replacing IOrganizer::FileInfo with FileInfo.
|
||||
WARNING: Replacing IOrganizer::FileInfo with FileInfo.
|
||||
WARNING: Replacing IPluginInstaller::EInstallResult with InstallResult.
|
||||
WARNING: Replacing IPluginInstaller::EInstallResult with InstallResult.
|
||||
```
|
||||
|
||||
As you can see, only a few types were manually fixed (specified in
|
||||
`config-2.4.yml`).
|
||||
only conversions or fixes considered "strange" will be shown.
|
||||
|
||||
## Configuration file
|
||||
|
||||
@@ -102,20 +83,37 @@ deduced by `main` (or are too complex to deduce), and the documentation for ever
|
||||
|
||||
## Uploading the stubs to pypi
|
||||
|
||||
The upload of the stubs to https://pypi.org/project/mobase-stubs/ should be
|
||||
done automatically when a new Github release is made.
|
||||
The upload of the stubs to [https://pypi.org/project/mobase-stubs/](https://pypi.org/project/mobase-stubs/)
|
||||
should be done automatically when a new Github tag is pushed.
|
||||
|
||||
## Extras — Starts a python interpreter with `mobase`
|
||||
## Extras — Using `mobase` in a Python interpreter
|
||||
|
||||
It is possible to start a (i)python interpret with `mobase` imported by running:
|
||||
It is possible to start a (i)python interpreter with `mobase` imported by running
|
||||
|
||||
```
|
||||
python -im generator.loader ${MO2_INSTALL_PATH}
|
||||
```bash
|
||||
python -i -m mo2.stubs.generator.loader ${MO2_INSTALL_PATH}
|
||||
```
|
||||
|
||||
This has no real usage except for MO2 developers since most classes from the `mobase` module cannot be instantiated.
|
||||
You can also import `mobase` in your code using the following (after installing
|
||||
this package):
|
||||
|
||||
# License
|
||||
```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
|
||||
for MO2 developers.
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
|
||||
+375
-56
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -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,4 +1,4 @@
|
||||
sphinx-rtd-theme
|
||||
sphinx-autodoc-typehints
|
||||
sphinx-automodapi
|
||||
PyQt5
|
||||
PyQt6
|
||||
|
||||
+2
-10
@@ -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"]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
@@ -1,190 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
import black
|
||||
import isort
|
||||
|
||||
from generator import LOGGER
|
||||
from generator.loader import load_mobase
|
||||
from generator.mtypes import Class, Function, PyType
|
||||
from generator.parser import is_enum
|
||||
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",
|
||||
metavar="INSTALL_DIR",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="installation directory of Mod Organizer 2",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=Path,
|
||||
default="stubs/setup/mobase-stubs/__init__.pyi",
|
||||
help="output file (default stubs/setup/mobase-stubs/__init__.pyi)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", action="store_true", help="verbose mode (all logs go to stderr)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
type=argparse.FileType("r"),
|
||||
default=None,
|
||||
help="configuration file",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig()
|
||||
LOGGER.setLevel(logging.WARNING)
|
||||
|
||||
if args.verbose:
|
||||
LOGGER.setLevel(logging.INFO)
|
||||
|
||||
output_path = cast(Path, args.output)
|
||||
|
||||
# Load settings from the configuration:
|
||||
settings: Settings = Settings(register=MOBASE_REGISTER)
|
||||
if args.config is not None:
|
||||
settings = Settings(MOBASE_REGISTER, args.config)
|
||||
|
||||
# Parse mobase:
|
||||
|
||||
# Load mobase (cannot simply do "import mobase"):
|
||||
mobase = load_mobase(Path(args.install_dir))
|
||||
|
||||
# List of objects:
|
||||
objects = []
|
||||
|
||||
for name in dir(mobase):
|
||||
if name.startswith("__"):
|
||||
continue
|
||||
|
||||
if name in settings.ignore_names:
|
||||
continue
|
||||
|
||||
# we do not want the real MoVariant
|
||||
if name == "MoVariant":
|
||||
continue
|
||||
|
||||
# ignore the private module
|
||||
if name == "private":
|
||||
continue
|
||||
|
||||
# for now, ignore this since it is a submodule and we
|
||||
# not handle them
|
||||
if name == "widgets":
|
||||
continue
|
||||
|
||||
# IPlugin is not the real object
|
||||
if name == "IPlugin":
|
||||
continue
|
||||
|
||||
objects.append((name, getattr(mobase, name)))
|
||||
|
||||
# enum first, and then alphabetical, should be fine with the __future__ import
|
||||
objects = sorted(
|
||||
objects, key=lambda e: (isinstance(e[1], type), not is_enum(e[1]), e[0])
|
||||
)
|
||||
|
||||
for n, o in objects:
|
||||
MOBASE_REGISTER.add_object(n, o)
|
||||
|
||||
# Process everything:
|
||||
for n, o in objects:
|
||||
|
||||
# Create the corresponding object:
|
||||
c = MOBASE_REGISTER.make_object(n, o)
|
||||
|
||||
if isinstance(c, Class):
|
||||
|
||||
# Clean the class (e.g., remove duplicates methods due to wrappers):
|
||||
clean_class(c, settings)
|
||||
|
||||
# Path the class using the configuration:
|
||||
settings.patch_class(c)
|
||||
|
||||
elif isinstance(c, list) and isinstance(c[0], Function):
|
||||
settings.patch_functions(c)
|
||||
|
||||
else:
|
||||
LOGGER.critical(
|
||||
"Cannot generated stubs for {}, unsupported object type.".format(n)
|
||||
)
|
||||
|
||||
# Write everything:
|
||||
with open(args.output, "w") as output:
|
||||
|
||||
writer = Writer(output, settings)
|
||||
|
||||
# the __future__ import must be at the beginning
|
||||
writer.print_imports([("__future__", ["annotations"])])
|
||||
writer.print_version(settings.mobase["__version__"]) # type: ignore
|
||||
writer.print_imports(
|
||||
[
|
||||
"abc",
|
||||
("enum", ["Enum"]),
|
||||
("pathlib", ["Path"]),
|
||||
(
|
||||
"typing",
|
||||
[
|
||||
"Dict",
|
||||
"Iterator",
|
||||
"List",
|
||||
"Tuple",
|
||||
"Union",
|
||||
"Any",
|
||||
"Optional",
|
||||
"Callable",
|
||||
"overload",
|
||||
"Sequence",
|
||||
"Set",
|
||||
"TypeVar",
|
||||
"Type",
|
||||
],
|
||||
),
|
||||
"PyQt6.QtCore",
|
||||
"PyQt6.QtGui",
|
||||
"PyQt6.QtWidgets",
|
||||
]
|
||||
)
|
||||
|
||||
# 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()
|
||||
|
||||
# This is a class to represent interface not implemented:
|
||||
writer.print_class(Class("InterfaceNotImplemented", [], []))
|
||||
writer._print()
|
||||
|
||||
for n, o in objects:
|
||||
|
||||
# Get the corresponding object:
|
||||
c = MOBASE_REGISTER.get_object(n)
|
||||
|
||||
if isinstance(c, Class):
|
||||
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(
|
||||
output_path,
|
||||
fast=False,
|
||||
mode=black.Mode(is_pyi=args.output.name.endswith("pyi")),
|
||||
write_back=black.WriteBack.YES,
|
||||
)
|
||||
isort.api.sort_file(output_path)
|
||||
Generated
+769
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
[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.0"
|
||||
pyyaml = "^6.0.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pyright = "^1.1.365"
|
||||
ruff = "^0.4.7"
|
||||
types-pyyaml = "^6.0.12.20240311"
|
||||
poethepoet = "^0.26.1"
|
||||
|
||||
[tool.poetry.group.doc.dependencies]
|
||||
sphinx-rtd-theme = "^2.0.0"
|
||||
sphinx-autodoc-typehints = "^2.1.0"
|
||||
sphinx-automodapi = "^0.16.0"
|
||||
sphinx = "^7.3.7"
|
||||
|
||||
[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.ruff.lint.isort]
|
||||
known-first-party = ['tas']
|
||||
|
||||
[tool.pyright]
|
||||
typeCheckingMode = "strict"
|
||||
reportMissingTypeStubs = true
|
||||
@@ -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 generator main.py --check --diff
|
||||
flake8 generator main.py
|
||||
mypy generator main.py
|
||||
isort -c generator main.py
|
||||
@@ -0,0 +1,7 @@
|
||||
import logging
|
||||
|
||||
from .loader import load_mobase
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ["load_mobase", "LOGGER"]
|
||||
@@ -0,0 +1,239 @@
|
||||
import argparse
|
||||
import inspect
|
||||
import logging
|
||||
import subprocess
|
||||
import types
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
from .loader import load_mobase
|
||||
from .mtypes import Class, 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: Sequence[str] = []
|
||||
) -> list[tuple[str, type]]:
|
||||
objects: list[tuple[str, type]] = []
|
||||
|
||||
assert hasattr(module, "__name__")
|
||||
module_name: str = module.__name__ # type: ignore
|
||||
|
||||
for name in dir(module):
|
||||
if name.startswith("__") or name in skips:
|
||||
continue
|
||||
|
||||
obj = getattr(module, name)
|
||||
|
||||
# skip submodules
|
||||
if inspect.ismodule(obj):
|
||||
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))
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
def add_mobase_header(writer: Writer):
|
||||
writer.print_imports(
|
||||
[
|
||||
"abc",
|
||||
("enum", ["Enum"]),
|
||||
"os",
|
||||
(
|
||||
"typing",
|
||||
[
|
||||
"Callable",
|
||||
"Dict",
|
||||
"Iterator",
|
||||
"Optional",
|
||||
"overload",
|
||||
"Sequence",
|
||||
"Type",
|
||||
"TypeVar",
|
||||
"Union",
|
||||
],
|
||||
),
|
||||
"PyQt6.QtCore",
|
||||
"PyQt6.QtGui",
|
||||
"PyQt6.QtWidgets",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def add_mobase_widgets_header(writer: Writer):
|
||||
writer.print_imports(
|
||||
[
|
||||
(
|
||||
"typing",
|
||||
["List", "Tuple", "Union", "overload"],
|
||||
),
|
||||
"PyQt6.QtCore",
|
||||
"PyQt6.QtGui",
|
||||
"PyQt6.QtWidgets",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser("stubs generator for the MO2 python interface")
|
||||
parser.add_argument(
|
||||
"install_dir",
|
||||
metavar="INSTALL_DIR",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="installation directory of Mod Organizer 2",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=Path,
|
||||
default=Path("stubs/setup/mobase-stubs"),
|
||||
help="output folder (default stubs/setup/mobase-stubs)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="verbose mode (all logs go to stderr)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="configuration file",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig()
|
||||
LOGGER.setLevel(logging.WARNING)
|
||||
|
||||
if args.verbose:
|
||||
LOGGER.setLevel(logging.INFO)
|
||||
|
||||
output_path: Path = args.output
|
||||
config_path: Path | None = args.config
|
||||
|
||||
# create the register
|
||||
register = MobaseRegister()
|
||||
|
||||
# load mobase (cannot simply do "import mobase")
|
||||
mobase = load_mobase(Path(args.install_dir))
|
||||
|
||||
# headers
|
||||
module_headers: dict[str, Callable[[Writer], None]] = {
|
||||
"mobase": add_mobase_header,
|
||||
"mobase.widgets": add_mobase_widgets_header,
|
||||
}
|
||||
|
||||
# list of objects directly in mobase
|
||||
module_objects: dict[str, list[tuple[str, type]]] = {
|
||||
"mobase": extract_objects(
|
||||
mobase,
|
||||
[
|
||||
# the "real" IPlugin is IPluginBase
|
||||
"IPlugin",
|
||||
],
|
||||
),
|
||||
"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:
|
||||
with open(config_path, "r") as fp:
|
||||
settings = Settings(register, fp, module=name)
|
||||
|
||||
for n, o in objects:
|
||||
register.add_object(n, o)
|
||||
|
||||
# enum first, and then alphabetical, should be fine with the __future__ import
|
||||
objects = sorted(
|
||||
objects, key=lambda e: (isinstance(e[1], type), not is_enum(e[1]), e[0])
|
||||
)
|
||||
|
||||
# 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 is_list_of_functions(c):
|
||||
settings.patch_functions(c)
|
||||
|
||||
else:
|
||||
LOGGER.critical(
|
||||
"Cannot generated stubs for {}, unsupported object type.".format(n)
|
||||
)
|
||||
|
||||
output_folder = output_path
|
||||
if name != "mobase":
|
||||
output_folder = output_path.joinpath(
|
||||
name.replace("mobase.", "").replace(".", "/")
|
||||
)
|
||||
|
||||
# create directory if required
|
||||
output_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 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
|
||||
writer.print_imports([("__future__", ["annotations"])])
|
||||
writer.print_version(settings.version)
|
||||
|
||||
module_headers[name](writer)
|
||||
|
||||
for n, _o in objects:
|
||||
# Get the corresponding object:
|
||||
c = register.get_object(n)
|
||||
|
||||
writer.print_object(c)
|
||||
|
||||
subprocess.run(
|
||||
[
|
||||
"ruff",
|
||||
"--silent",
|
||||
"format",
|
||||
output_folder.joinpath("__init__.pyi").as_posix(),
|
||||
]
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
"ruff",
|
||||
"--silent",
|
||||
"check",
|
||||
"--select",
|
||||
"I",
|
||||
"--fix",
|
||||
output_folder.joinpath("__init__.pyi").as_posix(),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,11 +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: Path):
|
||||
def load_mobase(path: os.PathLike[Any]) -> Module:
|
||||
"""
|
||||
Load the mobase from the given MO2 installation path and
|
||||
returns it.
|
||||
@@ -16,6 +16,8 @@ def load_mobase(path: Path):
|
||||
Returns: The mobase module.
|
||||
"""
|
||||
|
||||
path = Path(path)
|
||||
|
||||
# We need absolute path for loading DLL and modules:
|
||||
path = path.resolve()
|
||||
|
||||
@@ -27,19 +29,18 @@ def load_mobase(path: Path):
|
||||
[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())
|
||||
|
||||
import mobase
|
||||
import mobase # type: ignore
|
||||
|
||||
return mobase
|
||||
return mobase # type: ignore
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -1,8 +1,8 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from typing import Final, TypeVar
|
||||
|
||||
|
||||
class PyType:
|
||||
@@ -10,15 +10,6 @@ class PyType:
|
||||
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
|
||||
|
||||
def __init__(self, name: str | type):
|
||||
@@ -30,16 +21,9 @@ class PyType:
|
||||
|
||||
self.name = name.strip()
|
||||
|
||||
# remove MOBase:: and mobase.
|
||||
self.name = self.name.replace("mobase.", "")
|
||||
|
||||
# replace QFlags[xxx] with xxx
|
||||
self.name = re.sub(r"QFlags\[([^]]*)\]", r"\1", self.name)
|
||||
|
||||
# we replace QVariant with MoVariant which is valid python type
|
||||
if self.name == "QVariant":
|
||||
self.name = "MoVariant"
|
||||
|
||||
# find PyQt types
|
||||
for m in (QtCore, QtGui, QtWidgets):
|
||||
if self.name in dir(m):
|
||||
@@ -51,8 +35,12 @@ class PyType:
|
||||
A valid typing representation for this type.
|
||||
"""
|
||||
# IPluginBase -> IPlugin
|
||||
if self.name == "IPluginBase":
|
||||
if self.name == "mobase.IPluginBase":
|
||||
return "IPlugin"
|
||||
|
||||
# PathLike should be [] in the stubs
|
||||
self.name = self.name.replace("os.PathLike", "os.PathLike[str]")
|
||||
|
||||
return self.name
|
||||
|
||||
def is_none(self) -> bool:
|
||||
@@ -133,7 +121,6 @@ class Argument:
|
||||
|
||||
@property
|
||||
def value(self) -> str | None:
|
||||
|
||||
value = self._value
|
||||
|
||||
if value is None:
|
||||
@@ -142,10 +129,18 @@ class Argument:
|
||||
# pybind11 puts enum in <> so we need to fix
|
||||
m = re.match(r"<([^:]+):\s*[0-9]+>", value)
|
||||
if m:
|
||||
# we also need to use the upper case version
|
||||
value = m.group(1)
|
||||
parts = value.split(".")
|
||||
value = ".".join(parts[:-1] + [parts[-1].upper()])
|
||||
# if this is a mobase enum, we eed to use the upper case version
|
||||
if self.type.name.startswith("mobase"):
|
||||
value = m.group(1)
|
||||
parts = value.split(".")
|
||||
value = ".".join(parts[:-1] + [parts[-1].upper()])
|
||||
|
||||
# PyQt -> need to fix
|
||||
elif self.type.name.startswith("PyQt"):
|
||||
parts = m.group(1).split(".")
|
||||
value = f"{self.type.name}.{parts[-1]}"
|
||||
else:
|
||||
value = m.group(1)
|
||||
|
||||
return value
|
||||
|
||||
@@ -170,7 +165,6 @@ class Argument:
|
||||
|
||||
|
||||
class Exception:
|
||||
|
||||
"""
|
||||
Small class representing exception that can be raised from functions.
|
||||
"""
|
||||
@@ -317,21 +311,22 @@ class Class:
|
||||
|
||||
def __init__(
|
||||
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
|
||||
@@ -343,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.
|
||||
@@ -351,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.
|
||||
@@ -359,11 +354,21 @@ 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) -> str:
|
||||
"""
|
||||
Returns:
|
||||
The full name of this class, i.e., package.canonical_name.
|
||||
"""
|
||||
if self.package:
|
||||
return f"{self.package}.{self.canonical_name}"
|
||||
return self.canonical_name
|
||||
|
||||
@property
|
||||
def all_bases(self) -> set[Class]:
|
||||
"""
|
||||
@@ -378,12 +383,8 @@ class Class:
|
||||
def is_deprecated(self):
|
||||
return self.deprecated
|
||||
|
||||
def __str__(self):
|
||||
return self.canonical_name
|
||||
|
||||
|
||||
class PyClass(Class):
|
||||
|
||||
"""
|
||||
Class use to wrap Python class to be used as parent class for some classes
|
||||
in mobase.
|
||||
@@ -391,24 +392,27 @@ class PyClass(Class):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
package: str,
|
||||
name: str,
|
||||
):
|
||||
super().__init__(name, [], [])
|
||||
super().__init__(package, name, [], [])
|
||||
self.abstract = False
|
||||
|
||||
|
||||
class Enum(Class):
|
||||
|
||||
"""
|
||||
Class representing an enum.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, values: dict[str, int], methods: list[Method]):
|
||||
def __init__(
|
||||
self, package: str, name: str, values: dict[str, int], methods: list[Method]
|
||||
):
|
||||
# Note: Boost.Python.enum inherits int() not enum.Enum() but for the sake
|
||||
# of stubs, I think making them inherit enum.Enum is more appropriate:
|
||||
super().__init__(
|
||||
package,
|
||||
name,
|
||||
[PyClass("Enum")],
|
||||
[PyClass("", "Enum")],
|
||||
methods,
|
||||
inner_classes=[],
|
||||
constants=[Constant(k, None, v) for k, v in values.items()],
|
||||
@@ -416,3 +420,26 @@ class Enum(Class):
|
||||
|
||||
def is_abstract(self):
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user