diff --git a/docs/stubgen.py b/docs/stubgen.py index 91793e40d..144b149e3 100644 --- a/docs/stubgen.py +++ b/docs/stubgen.py @@ -520,8 +520,14 @@ def gen_class(file: Stream, class_obj: Type): def gen(module: types.ModuleType, destpath: str): + global dependencies, dummy_types, fwd_ref + begin = time.time() + dependencies.clear() + dummy_types.clear() + fwd_ref.clear() + output_basepath = os.path.join(destpath, module.__name__) if __file__ in dir(module): print( @@ -712,8 +718,7 @@ def gen(module: types.ModuleType, destpath: str): imports = collect_imports(func_deps) - needed_dummies = set(imports["."]).intersection(dummy_types) - needed_dummies = sorted(list(needed_dummies)) + needed_dummies = sorted(list(dummy_types)) # don't need to import anything locally, it's already going to be imported below in the classes del imports["."] @@ -797,9 +802,8 @@ def gen(module: types.ModuleType, destpath: str): needed_dummies.sort() out.println("") out.println("# Dummy types") - out.println("from typing import NewType") for dummy in needed_dummies: - out.println(f"{dummy} = NewType('{dummy}', int)") + out.println(f"from . import {dummy}") if len(dependencies[name]) > 0: out.println("") diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp index 7208d1e94..fd1a33f2c 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp @@ -64,6 +64,7 @@ PyTypeObject **SbkPySide2_QtWidgetsTypes = NULL; #include #include #include +#include #include #include #include @@ -220,6 +221,186 @@ void FetchException(QString &typeStr, QString &valueStr, int &finalLine, QList o.major; + if(minor != o.minor) + return minor > o.minor; + + // if we're identical major/minor, don't regenerate if it's the same commit + if(commit == o.commit) + return false; + + // never replace a stable version with non-stable + if(o.commit == lit("stable")) + return false; + + // otherwise we don't know when this was m ade, regenerate + return true; + } +}; + +QDebug operator<<(QDebug debug, const StubsVersion &ver) +{ + debug << QFormatStr("%1.%2 (%3)").arg(ver.major).arg(ver.minor).arg(ver.commit.mid(0, 6)); + return debug; +} + +StubsVersion GetStubsVersion(QString basePath) +{ + StubsVersion ret; + QFile file(QDir(basePath).absoluteFilePath(lit("version.txt"))); + if(file.open(QFile::ReadOnly)) + { + QByteArray version = file.readAll(); + QTextStream ts(version); + + ts >> ret.major >> ret.minor >> ret.commit; + + file.close(); + } + + return ret; +} + +void SetStubsVersion(QString basePath, const StubsVersion &ver) +{ + QFile file(QDir(basePath).absoluteFilePath(lit("version.txt"))); + if(file.open(QFile::WriteOnly | QFile::Truncate)) + { + QString version; + QTextStream ts(&version); + + ts << ver.major << "\n" << ver.minor << "\n" << ver.commit; + + file.write(version.toUtf8()); + + file.close(); + } +} +void PythonContext::GenerateStubs(const rdcarray &extraPaths) +{ + QElapsedTimer timer; + timer.start(); + + StubsVersion cur = StubsVersion::Current(); + + QStringList paths; + + // take the first standard location that works, these should be in priority order and typically + // the first one is writeable as normal. + for(QString path : QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)) + { + paths << GetPystubsLocations(path + lit("/pystubs")); + if(!paths.empty()) + break; + } + + for(rdcstr path : extraPaths) + paths << GetPystubsLocations(path); + + PyObject *stubgen_module = NULL; + PyObject *gen = NULL; + + for(QString target : paths) + { + StubsVersion ver = GetStubsVersion(target); + + if(!cur.ShouldReplace(ver)) + { + qInfo() << "Not modifying stubs of version " << ver << "in" << target; + continue; + } + + SetStubsVersion(target, cur); + + if(gen == NULL) + { + QByteArray stubgen; + { + QFile file(lit(":/py/stubgen.py")); + + file.open(QFile::ReadOnly); + stubgen = file.readAll(); + file.close(); + } + + PyObject *stubgen_compiled = Py_CompileString(stubgen.data(), "stubgen.py", Py_file_input); + stubgen_module = PyImport_ExecCodeModule("__rd_stubgen", stubgen_compiled); + Py_XDECREF(stubgen_compiled); + + gen = PyObject_GetAttrString(stubgen_module, "gen"); + } + + PyObject *args = + Py_BuildValue("(Os)", PyDict_GetItemString(main_dict, "renderdoc"), target.toUtf8().data()); + PyObject *retval = PyObject_CallObject(gen, args); + + if(!retval) + qCritical() << "Didn't generate renderdoc stubs"; + + Py_XDECREF(retval); + Py_XDECREF(args); + + args = + Py_BuildValue("(Os)", PyDict_GetItemString(main_dict, "qrenderdoc"), target.toUtf8().data()); + retval = PyObject_CallObject(gen, args); + + if(!retval) + qCritical() << "Didn't generate qrenderdoc stubs"; + + Py_XDECREF(retval); + Py_XDECREF(args); + + qInfo() << "Generated stubs for " << cur << "into" << target; + } + + Py_XDECREF(gen); + Py_XDECREF(stubgen_module); + + qInfo() << "Stubs generation processed in" << timer.elapsed() << "ms"; +} + void PythonContext::GlobalInit() { // must happen on the UI thread @@ -296,6 +477,8 @@ void PythonContext::GlobalInit() main_dict = PyModule_GetDict(main_module); + GenerateStubs({}); + // replace sys.stdout and sys.stderr with our own objects. These have a 'this' pointer of NULL, // which then indicates they need to forward to a global object diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.h b/qrenderdoc/Code/pyrenderdoc/PythonContext.h index f802cc3dd..35366f6dd 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.h +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.h @@ -65,6 +65,8 @@ public: rdcarray> &args); static void FreePyArgs(rdcarray> &args); + static void GenerateStubs(const rdcarray &extraPaths); + bool CheckInterfaces(rdcstr &log); QString versionString(); diff --git a/qrenderdoc/Resources/resources.qrc b/qrenderdoc/Resources/resources.qrc index fa750b87b..be0e57ef6 100644 --- a/qrenderdoc/Resources/resources.qrc +++ b/qrenderdoc/Resources/resources.qrc @@ -1,4 +1,7 @@ + + ../../docs/stubgen.py + logo.svg topologies/topo_linelist.svg