[neon/forks/sip6/Neon/release] /: New upstream version 6.7.3+dfsg
Dmitry Shachnev
null at kde.org
Sat Feb 4 02:04:45 GMT 2023
Git commit f1be3fc300d917c5b5eacf2d4cb6f845fc830b92 by Dmitry Shachnev.
Committed on 28/10/2022 at 16:57.
Pushed by carlosdem into branch 'Neon/release'.
New upstream version 6.7.3+dfsg
M +177 -0 ChangeLog
M +3 -0 NEWS
M +1 -1 PKG-INFO
D +0 -1110 code_generator/export.c
M +2 -2 code_generator/gencode.c
M +134 -134 code_generator/py2c.c
M +0 -50 code_generator/pybinding.c
M +4 -10 code_generator/sip.h
M +58 -984 code_generator/type_hints.c
M +1 -1 sip.egg-info/PKG-INFO
M +15 -14 sip.egg-info/SOURCES.txt
M +6 -5 sipbuild/bindings.py
M +4 -7 sipbuild/generator/instantiations.py
M +1 -0 sipbuild/generator/outputs/__init__.py
M +17 -14 sipbuild/generator/outputs/api.py
R +3 -0 sipbuild/generator/outputs/formatters/__init__.py [from: sipbuild/generator/formatters/__init__.py - 091% similarity]
R +150 -58 sipbuild/generator/outputs/formatters/argument.py [from: sipbuild/generator/formatters/argument.py - 070% similarity]
R +0 -0 sipbuild/generator/outputs/formatters/base_formatter.py [from: sipbuild/generator/formatters/base_formatter.py - 100% similarity]
R +53 -1 sipbuild/generator/outputs/formatters/enum.py [from: sipbuild/generator/formatters/enum.py - 050% similarity]
R +28 -3 sipbuild/generator/outputs/formatters/klass.py [from: sipbuild/generator/formatters/klass.py - 063% similarity]
R +1 -1 sipbuild/generator/outputs/formatters/overload.py [from: sipbuild/generator/formatters/overload.py - 098% similarity]
R +0 -0 sipbuild/generator/outputs/formatters/scoped.py [from: sipbuild/generator/formatters/scoped.py - 100% similarity]
R +11 -8 sipbuild/generator/outputs/formatters/signature.py [from: sipbuild/generator/formatters/signature.py - 080% similarity]
R +6 -9 sipbuild/generator/outputs/formatters/template.py [from: sipbuild/generator/formatters/template.py - 085% similarity]
R +33 -0 sipbuild/generator/outputs/formatters/utils.py [from: sipbuild/generator/formatters/utils.py - 066% similarity]
R +51 -11 sipbuild/generator/outputs/formatters/value_list.py [from: sipbuild/generator/formatters/value_list.py - 065% similarity]
R +10 -0 sipbuild/generator/outputs/formatters/variable.py [from: sipbuild/generator/formatters/variable.py - 079% similarity]
A +685 -0 sipbuild/generator/outputs/pyi.py
A +497 -0 sipbuild/generator/outputs/type_hints.py
A +418 -0 sipbuild/generator/outputs/xml.py
M +4 -2 sipbuild/generator/parser/parser.py
M +13 -16 sipbuild/generator/parser/parser_manager.py
M +6 -5 sipbuild/generator/resolver/resolver.py
M +5 -10 sipbuild/generator/specification.py
D +0 -70 sipbuild/generator/type_hints.py
M +2 -2 sipbuild/version.py
https://invent.kde.org/neon/forks/sip6/commit/f1be3fc300d917c5b5eacf2d4cb6f845fc830b92
diff --git a/ChangeLog b/ChangeLog
index 8b2d6ef..ea9bb1a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,182 @@
+2022-10-27 Phil Thompson <phil at riverbankcomputing.com>
+
+ * sipbuild/generator/outputs/pyi.py:
+ Fixed the .pyi file __eq__ and __ne__ entries.
+ [ea12891e29e2] [6.7.3] <6.7-maint>
+
+ * NEWS, sipbuild/generator/outputs/pyi.py:
+ Added the Python .pyi code to the repo.
+ [1246f197548e] <6.7-maint>
+
+ * sipbuild/generator/outputs/type_hints.py:
+ Brackets are allowed in non-typing module type hints.
+ [b0f99d3165dc] <6.7-maint>
+
+ * code_generator/gencode.c, code_generator/sip.h,
+ code_generator/type_hints.c:
+ Stripped out redundant C code.
+ [4dedf2303b4a] <6.7-maint>
+
+ * sipbuild/generator/outputs/type_hints.py:
+ Completed the Python implementation of type hints.
+ [ce9d2cb4dc51] <6.7-maint>
+
+ * sipbuild/generator/outputs/formatters/argument.py,
+ sipbuild/generator/outputs/formatters/enum.py,
+ sipbuild/generator/outputs/formatters/klass.py,
+ sipbuild/generator/outputs/formatters/utils.py,
+ sipbuild/generator/outputs/type_hints.py:
+ Debugging of new type hint code.
+ [1756cefda6bb] <6.7-maint>
+
+2022-10-25 Phil Thompson <phil at riverbankcomputing.com>
+
+ * NEWS, code_generator/pybinding.c, sipbuild/bindings.py,
+ sipbuild/generator/outputs/__init__.py,
+ sipbuild/generator/outputs/api.py,
+ sipbuild/generator/outputs/formatters/__init__.py,
+ sipbuild/generator/outputs/formatters/argument.py,
+ sipbuild/generator/outputs/formatters/enum.py,
+ sipbuild/generator/outputs/formatters/klass.py,
+ sipbuild/generator/outputs/formatters/signature.py,
+ sipbuild/generator/outputs/formatters/utils.py,
+ sipbuild/generator/outputs/formatters/value_list.py,
+ sipbuild/generator/outputs/formatters/variable.py,
+ sipbuild/generator/outputs/type_hints.py,
+ sipbuild/generator/outputs/xml.py,
+ sipbuild/generator/parser/parser.py,
+ sipbuild/generator/parser/parser_manager.py,
+ sipbuild/generator/specification.py:
+ Initial re-implementation of the .pyi file generation in Python.
+ [f81ba5225472] <6.7-maint>
+
+2022-10-23 Phil Thompson <phil at riverbankcomputing.com>
+
+ * code_generator/py2c.c:
+ Removed a couple of unused conversion functions.
+ [62dfbdfe52f8] <6.7-maint>
+
+ * code_generator/py2c.c:
+ Fixed a regression in the convertion of type hints to C.
+ [d5c985671fc1] <6.7-maint>
+
+ * sipbuild/generator/outputs/formatters/argument.py,
+ sipbuild/generator/outputs/formatters/signature.py,
+ sipbuild/generator/outputs/formatters/template.py,
+ sipbuild/generator/outputs/xml.py:
+ Fixes to the XML output.
+ [11942a8be37d] <6.7-maint>
+
+ * code_generator/py2c.c:
+ Fixed a regression in the converting of type hints to C.
+ [464398062a62] <6.7-maint>
+
+ * code_generator/py2c.c, sipbuild/generator/formatters/__init__.py,
+ sipbuild/generator/formatters/argument.py,
+ sipbuild/generator/formatters/base_formatter.py,
+ sipbuild/generator/formatters/enum.py,
+ sipbuild/generator/formatters/klass.py,
+ sipbuild/generator/formatters/overload.py,
+ sipbuild/generator/formatters/scoped.py,
+ sipbuild/generator/formatters/signature.py,
+ sipbuild/generator/formatters/template.py,
+ sipbuild/generator/formatters/utils.py,
+ sipbuild/generator/formatters/value_list.py,
+ sipbuild/generator/formatters/variable.py,
+ sipbuild/generator/instantiations.py,
+ sipbuild/generator/outputs/api.py,
+ sipbuild/generator/outputs/formatters/__init__.py,
+ sipbuild/generator/outputs/formatters/argument.py,
+ sipbuild/generator/outputs/formatters/base_formatter.py,
+ sipbuild/generator/outputs/formatters/enum.py,
+ sipbuild/generator/outputs/formatters/klass.py,
+ sipbuild/generator/outputs/formatters/overload.py,
+ sipbuild/generator/outputs/formatters/scoped.py,
+ sipbuild/generator/outputs/formatters/signature.py,
+ sipbuild/generator/outputs/formatters/template.py,
+ sipbuild/generator/outputs/formatters/utils.py,
+ sipbuild/generator/outputs/formatters/value_list.py,
+ sipbuild/generator/outputs/formatters/variable.py,
+ sipbuild/generator/outputs/type_hints.py,
+ sipbuild/generator/outputs/xml.py,
+ sipbuild/generator/parser/parser_manager.py,
+ sipbuild/generator/specification.py,
+ sipbuild/generator/type_hints.py:
+ Refactoring based on a type hint just being an optional string.
+ [fb794ebfa872] <6.7-maint>
+
+ * NEWS, sipbuild/generator/outputs/xml.py:
+ The XML is now generated as an etree root node rather than an XML
+ file.
+ [9b31d8115ad0] <6.7-maint>
+
+2022-10-22 Phil Thompson <phil at riverbankcomputing.com>
+
+ * sipbuild/generator/type_hints.py:
+ Completed the Python implementation of the XML generation.
+ [59c9a1dcd3dc] <6.7-maint>
+
+ * sipbuild/generator/type_hints.py:
+ Fixed the parsing of type hints for class enums defined with Python
+ scopes.
+ [46dfc03ef680] <6.7-maint>
+
+ * sipbuild/generator/type_hints.py:
+ Fixed the parsing of mapped type type hints.
+ [990804c44eec] <6.7-maint>
+
+ * sipbuild/generator/formatters/value_list.py,
+ sipbuild/generator/type_hints.py:
+ Debugging of XML generation.
+ [888053404043] <6.7-maint>
+
+ * code_generator/export.c, code_generator/pybinding.c,
+ code_generator/sip.h, sipbuild/generator/formatters/__init__.py,
+ sipbuild/generator/formatters/argument.py,
+ sipbuild/generator/formatters/enum.py,
+ sipbuild/generator/formatters/klass.py,
+ sipbuild/generator/formatters/signature.py,
+ sipbuild/generator/formatters/template.py,
+ sipbuild/generator/formatters/utils.py,
+ sipbuild/generator/formatters/value_list.py,
+ sipbuild/generator/outputs/xml.py,
+ sipbuild/generator/resolver/resolver.py,
+ sipbuild/generator/type_hints.py:
+ Completed the Python implementation of the XML generation.
+ [492f413d83bf] <6.7-maint>
+
+2022-10-20 Phil Thompson <phil at riverbankcomputing.com>
+
+ * Merged the latest maint branch changes.
+ [c014ebe8e7f2] <6.7-maint>
+
+2022-10-18 Phil Thompson <phil at riverbankcomputing.com>
+
+ * sipbuild/generator/formatters/argument.py,
+ sipbuild/generator/formatters/enum.py,
+ sipbuild/generator/formatters/klass.py,
+ sipbuild/generator/formatters/utils.py,
+ sipbuild/generator/formatters/value_list.py,
+ sipbuild/generator/formatters/variable.py,
+ sipbuild/generator/instantiations.py,
+ sipbuild/generator/outputs/xml.py,
+ sipbuild/generator/parser/parser_manager.py,
+ sipbuild/generator/type_hints.py:
+ Reimplemented enough of the XML output to represent a type.
+ [c9313bf161cb] <6.7-maint>
+
+2022-10-20 Phil Thompson <phil at riverbankcomputing.com>
+
+ * NEWS, sipbuild/generator/resolver/resolver.py:
+ Fixed a bug when logging resolver errors in overloads.
+ [3b317b873f00] <6.7-maint>
+
2022-10-12 Phil Thompson <phil at riverbankcomputing.com>
+ * .hgtags:
+ Added tag 6.7.2 for changeset 02e1e42540d2
+ [b359afe150b3] <6.7-maint>
+
* NEWS, sipbuild/bindings.py, sipbuild/generator/__init__.py,
sipbuild/generator/api.py, sipbuild/generator/extracts.py,
sipbuild/generator/outputs/__init__.py,
diff --git a/NEWS b/NEWS
index e1a2747..d459f60 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+v6.7.3 27th October 2022
+ - Bug fixes.
+
v6.7.2 12th October 2022
- Bug fixes.
diff --git a/PKG-INFO b/PKG-INFO
index 48fe4c5..be2f1a9 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: sip
-Version: 6.7.2
+Version: 6.7.3
Summary: A Python bindings generator for C/C++ libraries
Home-page: https://www.riverbankcomputing.com/software/sip/
Author: Riverbank Computing Limited
diff --git a/code_generator/export.c b/code_generator/export.c
deleted file mode 100644
index cd3c3f7..0000000
--- a/code_generator/export.c
+++ /dev/null
@@ -1,1110 +0,0 @@
-/*
- * The XML and API file generator module for SIP.
- *
- * Copyright (c) 2022 Riverbank Computing Limited <info at riverbankcomputing.com>
- *
- * This file is part of SIP.
- *
- * This copy of SIP is licensed for use under the terms of the SIP License
- * Agreement. See the file LICENSE for more details.
- *
- * This copy of SIP may also used under the terms of the GNU General Public
- * License v2 or v3 as published by the Free Software Foundation which can be
- * found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
- *
- * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "sip.h"
-
-
-#define XML_VERSION_NR 0 /* The schema version number. */
-
-/* Icon numbers. The values are those used by the eric IDE. */
-#define CLASS_ID 1
-#define METHOD_ID 4
-#define VARIABLE_ID 7
-#define ENUM_ID 10
-
-
-static void xmlClass(sipSpec *pt, moduleDef *mod, classDef *cd, FILE *fp);
-static void xmlEnums(sipSpec *pt, moduleDef *mod, classDef *scope, int indent,
- FILE *fp);
-static void xmlVars(sipSpec *pt, moduleDef *mod, classDef *scope, int indent,
- FILE *fp);
-static void xmlFunction(sipSpec *pt, moduleDef *mod, classDef *scope,
- memberDef *md, overDef *oloads, int indent, FILE *fp);
-static void xmlCtor(sipSpec *pt, moduleDef *mod, classDef *scope, ctorDef *ct,
- int indent, FILE *fp);
-static void xmlOverload(sipSpec *pt, moduleDef *mod, classDef *scope,
- memberDef *md, overDef *od, classDef *xtnds, int stat, int indent,
- FILE *fp);
-static void xmlCppSignature(FILE *fp, signatureDef *sd, int is_const);
-static void xmlArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
- KwArgs kwargs, int res_xfer, int indent, FILE *fp);
-static void xmlType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
- KwArgs kwargs, FILE *fp);
-static void xmlIndent(int indent, FILE *fp);
-static void xmlRealName(scopedNameDef *fqcname, const char *member, FILE *fp);
-static void xmlRealScopedName(classDef *scope, const char *cppname, FILE *fp);
-static const char *pyType(sipSpec *pt, argDef *ad, classDef **scope);
-static void restPyEnumMember(enumMemberDef *emd, FILE *fp);
-static void restPyAttribute(moduleDef *mod, classDef *scope, nameDef *name,
- FILE *fp);
-static int restValue(sipSpec *pt, valueDef *value, FILE *fp);
-static const char *reflectedSlot(slotType st);
-static int hasCppSignature(signatureDef *sd);
-static void appendScopedName(scopedNameDef **headp, scopedNameDef *newsnd);
-static void freeScopedName(scopedNameDef *snd);
-static scopedNameDef *text2scopePart(char *text);
-
-
-/*
- * Generate the XML export file.
- */
-int generateXML(sipSpec *pt, moduleDef *mod, const char *xmlFile)
-{
- FILE *fp;
- classDef *cd;
- memberDef *md;
-
- if ((fp = fopen(xmlFile, "w")) == NULL)
- return error("Unable to create file \"%s\"\n", xmlFile);
-
- fprintf(fp, "<?xml version=\"1.0\"?>\n");
- fprintf(fp, "<Module version=\"%u\" name=\"%s\">\n",
- XML_VERSION_NR, mod->name);
-
- /*
- * Note that we don't yet handle mapped types, templates or exceptions.
- */
-
- for (cd = pt->classes; cd != NULL; cd = cd->next)
- {
- if (cd->iff->module != mod)
- continue;
-
- if (isExternal(cd))
- continue;
-
- xmlClass(pt, mod, cd, fp);
- }
-
- for (cd = mod->proxies; cd != NULL; cd = cd->next)
- xmlClass(pt, mod, cd, fp);
-
- xmlEnums(pt, mod, NULL, 1, fp);
- xmlVars(pt, mod, NULL, 1, fp);
-
- for (md = mod->othfuncs; md != NULL; md = md->next)
- xmlFunction(pt, mod, NULL, md, mod->overs, 1, fp);
-
- fprintf(fp, "</Module>\n");
-
- fclose(fp);
-
- return 0;
-}
-
-
-/*
- * Generate a 'realname' attribute containing a fully qualified C/C++ name of
- * an object.
- */
-static void xmlRealScopedName(classDef *scope, const char *cppname, FILE *fp)
-{
- const char *sep = "";
-
- fprintf(fp, " realname=\"");
-
- if (scope != NULL)
- {
- scopedNameDef *snd;
-
- for (snd = removeGlobalScope(classFQCName(scope)); snd != NULL; snd = snd->next)
- {
- fprintf(fp, "%s%s", sep, snd->name);
- sep = "::";
- }
- }
-
- fprintf(fp, "%s%s\"", sep, cppname);
-}
-
-
-/*
- * Generate a 'realname' attribute containing a fully qualified C/C++ name.
- */
-static void xmlRealName(scopedNameDef *fqcname, const char *member, FILE *fp)
-{
- const char *sep = "";
- scopedNameDef *snd;
-
- fprintf(fp, " realname=\"");
-
- for (snd = removeGlobalScope(fqcname); snd != NULL; snd = snd->next)
- {
- fprintf(fp, "%s%s", sep, snd->name);
- sep = "::";
- }
-
- if (member != NULL)
- fprintf(fp, "::%s", member);
-
- fprintf(fp, "\"");
-}
-
-
-/*
- * Generate the XML for a class.
- */
-static void xmlClass(sipSpec *pt, moduleDef *mod, classDef *cd, FILE *fp)
-{
- int indent = 1;
- ctorDef *ct;
- memberDef *md;
-
- if (isOpaque(cd))
- {
- xmlIndent(indent, fp);
- fprintf(fp, "<OpaqueClass name=\"");
- prScopedPythonName(fp, cd->ecd, cd->pyname->text);
- fprintf(fp, "\"/>\n");
-
- return;
- }
-
- if (!isHiddenNamespace(cd))
- {
- xmlIndent(indent++, fp);
- fprintf(fp, "<Class name=\"");
- prScopedPythonName(fp, cd->ecd, cd->pyname->text);
- fprintf(fp, "\"");
-
- xmlRealName(classFQCName(cd), NULL, fp);
-
- if (cd->picklecode != NULL)
- fprintf(fp, " pickle=\"1\"");
-
- if (cd->convtocode != NULL)
- fprintf(fp, " convert=\"1\"");
-
- if (cd->convfromcode != NULL)
- fprintf(fp, " convertfrom=\"1\"");
-
- if (cd->real != NULL)
- fprintf(fp, " extends=\"%s\"", cd->real->iff->module->name);
-
- if (cd->pyqt_flags_enums != NULL)
- {
- const char *sep;
- stringList *sl;
-
- fprintf(fp, " flagsenums=\"");
- sep = "";
-
- for (sl = cd->pyqt_flags_enums; sl != NULL; sl = sl->next)
- {
- fprintf(fp, "%s%s", sep, sl->s);
- sep = " ";
- }
-
- fprintf(fp, "\"");
- }
-
- if (cd->supers != NULL)
- {
- classList *cl;
-
- fprintf(fp, " inherits=\"");
-
- for (cl = cd->supers; cl != NULL; cl = cl->next)
- {
- if (cl != cd->supers)
- fprintf(fp, " ");
-
- restPyClass(cl->cd, fp);
- }
-
- fprintf(fp, "\"");
- }
-
- fprintf(fp, ">\n");
- }
-
- for (ct = cd->ctors; ct != NULL; ct = ct->next)
- {
- if (isPrivateCtor(ct))
- continue;
-
- xmlCtor(pt, mod, cd, ct, indent, fp);
- }
-
- xmlEnums(pt, mod, cd, indent, fp);
- xmlVars(pt, mod, cd, indent, fp);
-
- for (md = cd->members; md != NULL; md = md->next)
- xmlFunction(pt, mod, cd, md, cd->overs, indent, fp);
-
- if (!isHiddenNamespace(cd))
- {
- xmlIndent(--indent, fp);
- fprintf(fp, "</Class>\n");
- }
-}
-
-
-/*
- * Generate the XML for all the enums in a scope.
- */
-static void xmlEnums(sipSpec *pt, moduleDef *mod, classDef *scope, int indent,
- FILE *fp)
-{
- enumDef *ed;
-
- for (ed = pt->enums; ed != NULL; ed = ed->next)
- {
- if (ed->module != mod)
- continue;
-
- if (ed->ecd != scope)
- continue;
-
- if (ed->pyname != NULL)
- {
- enumMemberDef *emd;
-
- xmlIndent(indent++, fp);
- fprintf(fp, "<Enum name=\"");
- prScopedPythonName(fp, ed->ecd, ed->pyname->text);
- fprintf(fp, "\"");
-
- xmlRealName(ed->fqcname, NULL, fp);
-
- fprintf(fp, ">\n");
-
- for (emd = ed->members; emd != NULL; emd = emd->next)
- {
- xmlIndent(indent, fp);
- fprintf(fp, "<EnumMember name=\"");
- prScopedPythonName(fp, ed->ecd, ed->pyname->text);
- fprintf(fp, ".%s\"", emd->pyname->text);
-
- xmlRealName(ed->fqcname, emd->cname, fp);
-
- fprintf(fp, "/>\n");
- }
-
- xmlIndent(--indent, fp);
- fprintf(fp, "</Enum>\n");
- }
- else
- {
- enumMemberDef *emd;
-
- for (emd = ed->members; emd != NULL; emd = emd->next)
- {
- xmlIndent(indent, fp);
- fprintf(fp, "<Member name=\"");
- prScopedPythonName(fp, ed->ecd, emd->pyname->text);
- fprintf(fp, "\"");
-
- xmlRealScopedName(scope, emd->cname, fp);
-
- fprintf(fp, " const=\"1\" typename=\"int\"/>\n");
- }
- }
- }
-}
-
-
-/*
- * Generate the XML for all the variables in a scope.
- */
-static void xmlVars(sipSpec *pt, moduleDef *mod, classDef *scope, int indent,
- FILE *fp)
-{
- varDef *vd;
-
- for (vd = pt->vars; vd != NULL; vd = vd->next)
- {
- if (vd->module != mod)
- continue;
-
- if (vd->ecd != scope)
- continue;
-
- xmlIndent(indent, fp);
- fprintf(fp, "<Member name=\"");
- prScopedPythonName(fp, vd->ecd, vd->pyname->text);
- fprintf(fp, "\"");
-
- xmlRealName(vd->fqcname, NULL, fp);
-
- if (isConstArg(&vd->type) || scope == NULL)
- fprintf(fp, " const=\"1\"");
-
- if (isStaticVar(vd))
- fprintf(fp, " static=\"1\"");
-
- xmlType(pt, mod, &vd->type, FALSE, NoKwArgs, fp);
- fprintf(fp, "/>\n");
- }
-}
-
-
-/*
- * Generate the XML for a ctor.
- */
-static void xmlCtor(sipSpec *pt, moduleDef *mod, classDef *scope, ctorDef *ct,
- int indent, FILE *fp)
-{
- int a;
-
- xmlIndent(indent++, fp);
- fprintf(fp, "<Function name=\"");
- prScopedPythonName(fp, scope, "__init__");
- fprintf(fp, "\"");
-
- xmlRealScopedName(scope, "__init__", fp);
-
- if (hasCppSignature(ct->cppsig))
- {
- fprintf(fp, " cppsig=\"");
- xmlCppSignature(fp, ct->cppsig, FALSE);
- fprintf(fp, "\"");
- }
-
- /* Handle the trivial case. */
- if (ct->pysig.nrArgs == 0)
- {
- fprintf(fp, "/>\n");
- return;
- }
-
- fprintf(fp, ">\n");
-
- for (a = 0; a < ct->pysig.nrArgs; ++a)
- {
- argDef *ad = &ct->pysig.args[a];
-
- if (isInArg(ad))
- xmlArgument(pt, mod, ad, FALSE, ct->kwargs, FALSE, indent, fp);
-
- if (isOutArg(ad))
- xmlArgument(pt, mod, ad, TRUE, ct->kwargs, FALSE, indent, fp);
- }
-
- xmlIndent(--indent, fp);
- fprintf(fp, "</Function>\n");
-}
-
-
-/*
- * Generate the XML for a function.
- */
-static void xmlFunction(sipSpec *pt, moduleDef *mod, classDef *scope,
- memberDef *md, overDef *oloads, int indent, FILE *fp)
-{
- overDef *od;
-
- for (od = oloads; od != NULL; od = od->next)
- {
- int isstat;
- classDef *xtnds;
-
- if (od->common != md)
- continue;
-
- if (isPrivate(od))
- continue;
-
- if (isSignal(od))
- {
- int a;
-
- xmlIndent(indent++, fp);
- fprintf(fp, "<Signal name=\"");
- prScopedPythonName(fp, scope, md->pyname->text);
- fprintf(fp, "\"");
-
- xmlRealScopedName(scope, od->cppname, fp);
-
- if (hasCppSignature(od->cppsig))
- {
- fprintf(fp, " cppsig=\"");
- xmlCppSignature(fp, od->cppsig, FALSE);
- fprintf(fp, "\"");
- }
-
- /* Handle the trivial case. */
- if (od->pysig.nrArgs == 0)
- {
- fprintf(fp, "/>\n");
- continue;
- }
-
- fprintf(fp, ">\n");
-
- for (a = 0; a < od->pysig.nrArgs; ++a)
- {
- argDef *ad = &od->pysig.args[a];
-
- xmlArgument(pt, mod, ad, FALSE, od->kwargs, FALSE, indent, fp);
- }
-
- xmlIndent(--indent, fp);
- fprintf(fp, "</Signal>\n");
-
- continue;
- }
-
- xtnds = NULL;
- isstat = (scope == NULL || scope->iff->type == namespace_iface || isStatic(od));
-
- if (scope == NULL && md->slot != no_slot && od->pysig.args[0].atype == class_type)
- {
- xtnds = od->pysig.args[0].u.cd;
- isstat = FALSE;
- }
-
- xmlOverload(pt, mod, scope, md, od, xtnds, isstat, indent, fp);
- }
-}
-
-
-/*
- * Generate the XML for an overload.
- */
-static void xmlOverload(sipSpec *pt, moduleDef *mod, classDef *scope,
- memberDef *md, overDef *od, classDef *xtnds, int stat, int indent,
- FILE *fp)
-{
- const char *name, *cppname = od->cppname;
- int a, no_res;
-
- xmlIndent(indent++, fp);
- fprintf(fp, "<Function name=\"");
-
- if (isReflected(od))
- {
- if ((name = reflectedSlot(md->slot)) != NULL)
- cppname = name;
- else
- name = md->pyname->text;
- }
- else
- {
- name = md->pyname->text;
- }
-
- prScopedPythonName(fp, scope, name);
-
- fprintf(fp, "\"");
-
- xmlRealScopedName(scope, cppname, fp);
-
- if (hasCppSignature(od->cppsig))
- {
- fprintf(fp, " cppsig=\"");
- xmlCppSignature(fp, od->cppsig, isConst(od));
- fprintf(fp, "\"");
- }
-
- if (isAbstract(od))
- fprintf(fp, " abstract=\"1\"");
-
- if (stat)
- fprintf(fp, " static=\"1\"");
-
- if (isSlot(od))
- fprintf(fp, " slot=\"1\"");
-
- if (isVirtual(od))
- {
- fprintf(fp, " virtual=\"1\"");
- }
-
- if (xtnds != NULL)
- {
- fprintf(fp, " extends=\"");
- prScopedPythonName(fp, xtnds->ecd, xtnds->pyname->text);
- fprintf(fp, "\"");
- }
-
- /* An empty type hint specifies a void return. */
- if (od->pysig.result.typehint_out != NULL && od->pysig.result.typehint_out->raw_hint[0] == '\0')
- no_res = TRUE;
- else
- no_res = (od->pysig.result.atype == void_type && od->pysig.result.nrderefs == 0);
-
- /* Handle the trivial case. */
- if (no_res && od->pysig.nrArgs == 0)
- {
- fprintf(fp, "/>\n");
- return;
- }
-
- fprintf(fp, ">\n");
-
- if (!no_res)
- xmlArgument(pt, mod, &od->pysig.result, TRUE, NoKwArgs,
- isResultTransferredBack(od), indent, fp);
-
- for (a = 0; a < od->pysig.nrArgs; ++a)
- {
- argDef *ad = &od->pysig.args[a];
-
- /*
- * Ignore the first argument of non-reflected number slots and the
- * second argument of reflected number slots.
- */
- if (isNumberSlot(md) && od->pysig.nrArgs == 2)
- if ((a == 0 && !isReflected(od)) || (a == 1 && isReflected(od)))
- continue;
-
- if (isInArg(ad))
- xmlArgument(pt, mod, ad, FALSE, od->kwargs, FALSE, indent, fp);
-
- if (isOutArg(ad))
- xmlArgument(pt, mod, ad, TRUE, od->kwargs, FALSE, indent, fp);
- }
-
- xmlIndent(--indent, fp);
- fprintf(fp, "</Function>\n");
-}
-
-
-/*
- * Return TRUE if there is a C/C++ signature.
- */
-static int hasCppSignature(signatureDef *sd)
-{
- int a;
-
- if (sd == NULL)
- return FALSE;
-
- /*
- * See if there are any arguments that could only have come from
- * handwritten code.
- */
- for (a = 0; a < sd->nrArgs; ++a)
- {
- switch (sd->args[a].atype)
- {
- case pyobject_type:
- case pytuple_type:
- case pylist_type:
- case pydict_type:
- case pycallable_type:
- case pyslice_type:
- case pytype_type:
- case pybuffer_type:
- case pyenum_type:
- case capsule_type:
- return FALSE;
-
- default:
- break;
- }
- }
-
- return TRUE;
-}
-
-
-/*
- * Generate the XML for a C++ signature.
- */
-static void xmlCppSignature(FILE *fp, signatureDef *sd, int is_const)
-{
- int a;
-
- prcode(fp, "%M");
- normaliseArgs(sd);
-
- prcode(fp, "(");
-
- for (a = 0; a < sd->nrArgs; ++a)
- {
- argDef *ad = &sd->args[a];
-
- if (a > 0)
- prcode(fp, ",");
-
- generateBaseType(NULL, ad, TRUE, STRIP_GLOBAL, fp);
- }
-
- prcode(fp, ")%s", (is_const ? " const" : ""));
-
- restoreArgs(sd);
- prcode(fp, "%M");
-}
-
-
-/*
- * Generate the XML for an argument.
- */
-static void xmlArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
- KwArgs kwargs, int res_xfer, int indent, FILE *fp)
-{
- if (isArraySize(ad))
- return;
-
- xmlIndent(indent, fp);
- fprintf(fp, "<%s", (out ? "Return" : "Argument"));
- xmlType(pt, mod, ad, out, kwargs, fp);
-
- if (!out)
- {
- if (isAllowNone(ad))
- fprintf(fp, " allownone=\"1\"");
-
- if (isDisallowNone(ad))
- fprintf(fp, " disallownone=\"1\"");
-
- if (isTransferred(ad))
- fprintf(fp, " transfer=\"to\"");
- else if (isThisTransferred(ad))
- fprintf(fp, " transfer=\"this\"");
- }
-
- if (res_xfer || isTransferredBack(ad))
- fprintf(fp, " transfer=\"back\"");
-
- fprintf(fp, "/>\n");
-}
-
-
-/*
- * Generate the XML for a type.
- */
-static void xmlType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
- KwArgs kwargs, FILE *fp)
-{
- const char *type_name;
- classDef *type_scope;
- typeHintDef *thd;
-
- fprintf(fp, " typename=\"");
-
- /* Handle the argument name. */
- if (!out && ad->name != NULL)
- {
- if (kwargs == AllKwArgs || (kwargs == OptionalKwArgs && ad->defval != NULL))
- fprintf(fp, "%s: ", ad->name->text);
- }
-
- /* Use any explicit type hint unless the argument is constrained. */
- thd = (out ? ad->typehint_out : (isConstrained(ad) ? NULL : ad->typehint_in));
-
- if (thd != NULL)
- {
- pyiTypeHint(pt, thd, mod, out, NULL, FALSE, TRUE, fp);
- }
- else
- {
- switch (ad->atype)
- {
- case class_type:
- restPyClass(ad->u.cd, fp);
- break;
-
- case enum_type:
- if (ad->u.ed->pyname != NULL)
- restPyEnum(ad->u.ed, fp);
- else
- fprintf(fp, "int");
-
- break;
-
- case mapped_type:
- /* There would normally be a type hint. */
- fprintf(fp, "unknown-type");
- break;
-
- default:
- if ((type_name = pyType(pt, ad, &type_scope)) != NULL)
- prScopedPythonName(fp, type_scope, type_name);
- }
- }
-
- if (!out && ad->name != NULL && ad->defval != NULL)
- {
- fprintf(fp, " = ");
-
- /*
- * Try and convert the value to a reST reference. We don't try very
- * hard but will get most cases.
- */
- if (!restValue(pt, ad->defval, fp))
- prDefaultValue(ad, FALSE, fp);
- }
-
- fprintf(fp, "\"");
-}
-
-
-/*
- * Generate the indentation for a line.
- */
-static void xmlIndent(int indent, FILE *fp)
-{
- while (indent-- > 0)
- fprintf(fp, " ");
-}
-
-
-/*
- * Get the Python representation of a type.
- */
-static const char *pyType(sipSpec *pt, argDef *ad, classDef **scope)
-{
- const char *type_name;
-
- *scope = NULL;
-
- switch (ad->atype)
- {
- case class_type:
- *scope = ad->u.cd->ecd;
- type_name = ad->u.cd->pyname->text;
- break;
-
- case mapped_type:
- if (ad->u.mtd->pyname != NULL)
- type_name = ad->u.mtd->pyname->text;
- else
- type_name = "unknown-type";
-
- break;
-
- case capsule_type:
- type_name = scopedNameTail(ad->u.cap);
- break;
-
- case struct_type:
- case union_type:
- case void_type:
- type_name = "sip.voidptr";
- break;
-
- case enum_type:
- if (ad->u.ed->pyname != NULL)
- {
- type_name = ad->u.ed->pyname->text;
- *scope = ad->u.ed->ecd;
- }
- else
- type_name = "int";
- break;
-
- case ustring_type:
- /* Correct for Python v3. */
- type_name = "bytes";
- break;
-
- case string_type:
- case sstring_type:
- case wstring_type:
- case ascii_string_type:
- case latin1_string_type:
- case utf8_string_type:
- type_name = isArray(ad) ? "bytes" : "str";
- break;
-
- case byte_type:
- case sbyte_type:
- case ubyte_type:
- case ushort_type:
- case uint_type:
- case long_type:
- case longlong_type:
- case ulong_type:
- case ulonglong_type:
- case short_type:
- case int_type:
- case cint_type:
- case ssize_type:
- case size_type:
- case hash_type:
- type_name = "int";
- break;
-
- case float_type:
- case cfloat_type:
- case double_type:
- case cdouble_type:
- type_name = "float";
- break;
-
- case bool_type:
- case cbool_type:
- type_name = "bool";
- break;
-
- case pyobject_type:
- type_name = "object";
- break;
-
- case pytuple_type:
- type_name = "tuple";
- break;
-
- case pylist_type:
- type_name = "list";
- break;
-
- case pydict_type:
- type_name = "dict";
- break;
-
- case pycallable_type:
- type_name = "callable";
- break;
-
- case pyslice_type:
- type_name = "slice";
- break;
-
- case pytype_type:
- type_name = "type";
- break;
-
- case pybuffer_type:
- type_name = "buffer";
- break;
-
- case pyenum_type:
- type_name = "enum";
- break;
-
- case ellipsis_type:
- type_name = "...";
- break;
-
- default:
- type_name = NULL;
- }
-
- return type_name;
-}
-
-
-/*
- * Generate a fully qualified attribute name as a reST reference.
- */
-static void restPyEnumMember(enumMemberDef *emd, FILE *fp)
-{
- fprintf(fp, ":sip:ref:`~%s.", emd->ed->module->fullname->text);
- prScopedPythonName(fp, emd->ed->ecd, emd->ed->pyname->text);
- fprintf(fp, ".%s`", emd->pyname->text);
-}
-
-
-/*
- * Generate a fully qualified attribute name as a reST reference.
- */
-static void restPyAttribute(moduleDef *mod, classDef *scope, nameDef *name,
- FILE *fp)
-{
- fprintf(fp, ":sip:ref:`~%s.", mod->fullname->text);
- prScopedPythonName(fp, scope, name->text);
- fprintf(fp, "`");
-}
-
-
-/*
- * Generate a reST reference for a scoped name if possible. Return TRUE if
- * something was generated.
- */
-static int restValue(sipSpec *pt, valueDef *value, FILE *fp)
-{
- const char *name;
- scopedNameDef *target, *scope, *snd;
- varDef *vd;
- enumDef *ed;
-
- /* The value must be a scoped name and we don't handle expressions. */
- if (value->vtype != scoped_value || value->next != NULL)
- return FALSE;
-
- target = value->u.vscp;
-
- /* See if it is an attribute. */
- for (vd = pt->vars; vd != NULL; vd = vd->next)
- if (compareScopedNames(vd->fqcname, target) == 0)
- {
- restPyAttribute(vd->module, vd->ecd, vd->pyname, fp);
-
- return TRUE;
- }
-
- /* Get the name and scope. */
- name = scopedNameTail(target);
-
- scope = NULL;
-
- for (snd = target; snd->name != name; snd = snd->next)
- appendScopedName(&scope, text2scopePart(snd->name));
-
- /* See if it is an enum member. */
- for (ed = pt->enums; ed != NULL; ed = ed->next)
- {
- enumMemberDef *emd;
-
- /*
- * Look for the member name first before working out if it is the
- * correct enum.
- */
- for (emd = ed->members; emd != NULL; emd = emd->next)
- {
- if (strcmp(emd->cname, name) == 0)
- {
- if (isScopedEnum(ed))
- {
- /*
- * It's a scoped enum so the fully qualified name of the
- * enum must match the scope of the name.
- */
- if (scope != NULL && compareScopedNames(ed->fqcname, scope) == 0)
- {
- restPyEnumMember(emd, fp);
-
- freeScopedName(scope);
-
- return TRUE;
- }
- }
- else
- {
- /*
- * It's a traditional enum so the scope of the enum must
- * match the scope of the name.
- */
- if ((ed->ecd == NULL && scope == NULL) || (ed->ecd != NULL && scope != NULL && compareScopedNames(ed->ecd->iff->fqcname, scope) == 0))
- {
- if (ed->fqcname == NULL)
- restPyAttribute(ed->module, ed->ecd, emd->pyname,
- fp);
- else
- restPyEnumMember(emd, fp);
-
- freeScopedName(scope);
-
- return TRUE;
- }
- }
-
- break;
- }
- }
- }
-
- freeScopedName(scope);
-
- return FALSE;
-}
-
-
-/*
- * Return the name of the reflected version of a slot or NULL if it doesn't
- * have one.
- */
-static const char *reflectedSlot(slotType st)
-{
- switch (st)
- {
- case add_slot:
- return "__radd__";
-
- case sub_slot:
- return "__rsub__";
-
- case mul_slot:
- return "__rmul__";
-
- case matmul_slot:
- return "__rmatmul__";
-
- case truediv_slot:
- return "__rtruediv__";
-
- case floordiv_slot:
- return "__rfloordiv__";
-
- case mod_slot:
- return "__rmod__";
-
- case lshift_slot:
- return "__rlshift__";
-
- case rshift_slot:
- return "__rrshift__";
-
- case and_slot:
- return "__rand__";
-
- case or_slot:
- return "__ror__";
-
- case xor_slot:
- return "__rxor__";
-
- default:
- break;
- }
-
- return NULL;
-}
-
-
-/*
- * Append a name to a list of scopes.
- */
-static void appendScopedName(scopedNameDef **headp, scopedNameDef *newsnd)
-{
- while (*headp != NULL)
- headp = &(*headp)->next;
-
- *headp = newsnd;
-}
-
-
-/*
- * Free a scoped name - but not the text itself.
- */
-static void freeScopedName(scopedNameDef *snd)
-{
- while (snd != NULL)
- {
- scopedNameDef *next = snd->next;
-
- free(snd);
-
- snd = next;
- }
-}
-
-
-/*
- * Convert a text string to a scope part structure.
- */
-static scopedNameDef *text2scopePart(char *text)
-{
- scopedNameDef *snd;
-
- snd = sipMalloc(sizeof (scopedNameDef));
-
- snd->name = text;
- snd->next = NULL;
-
- return snd;
-}
diff --git a/code_generator/gencode.c b/code_generator/gencode.c
index 0b61abb..0ca5354 100644
--- a/code_generator/gencode.c
+++ b/code_generator/gencode.c
@@ -14904,7 +14904,7 @@ static void generateCtorAutoDocstring(sipSpec *pt, classDef *cd, ctorDef *ct,
{
if (docstrings)
{
- pyiCtor(pt, pt->module, cd, ct, FALSE, NULL, 0, fp);
+ pyiCtor(pt, pt->module, cd, ct, fp);
++currentLineNr;
}
}
@@ -15840,5 +15840,5 @@ memberDef *findMethod(classDef *cd, const char *name)
*/
static void dsOverload(sipSpec *pt, overDef *od, int is_method, FILE *fp)
{
- pyiOverload(pt, pt->module, od, FALSE, is_method, NULL, 0, FALSE, fp);
+ pyiOverload(pt, pt->module, od, is_method, fp);
}
diff --git a/code_generator/py2c.c b/code_generator/py2c.c
index a734851..af2f708 100644
--- a/code_generator/py2c.c
+++ b/code_generator/py2c.c
@@ -62,21 +62,30 @@
#define TRANSFER_THIS 3
-/* Support for object caches. */
+/* Support for caches. */
typedef struct _objectCache {
PyObject *py_obj; /* The original Python object. */
void *c_struct; /* The converted C structure. */
struct _objectCache *next; /* The next in the list. */
} objectCache;
-static void cache(objectCache **head, PyObject *py_obj, void *c_struct);
-static void clear_cache(objectCache **head);
+typedef struct _strCache {
+ char *py_str; /* The original Python string. */
+ void *c_struct; /* The converted C structure. */
+ struct _strCache *next; /* The next in the list. */
+} strCache;
+
static void clear_caches(void);
-static void *search_cache(const objectCache *cache, PyObject *py_obj);
+static void cache_object(objectCache **head, PyObject *py_obj, void *c_struct);
+static void clear_object_cache(objectCache **head);
+static void *search_object_cache(const objectCache *cache, PyObject *py_obj);
+static void cache_str(strCache **head, char *py_str, void *c_struct);
+static void clear_str_cache(strCache **head);
+static void *search_str_cache(const strCache *cache, char *py_str);
/*
- * The object caches.
+ * The caches.
*/
static objectCache *cache_cachedname = NULL;
static objectCache *cache_class = NULL;
@@ -88,38 +97,38 @@ static objectCache *cache_mappedtype = NULL;
static objectCache *cache_member = NULL;
static objectCache *cache_module = NULL;
static objectCache *cache_qual = NULL;
-static objectCache *cache_typehint = NULL;
static objectCache *cache_virtualerrorhandler = NULL;
static objectCache *cache_wrappedenum = NULL;
static objectCache *cache_wrappedtypedef = NULL;
+static strCache *cache_typehint = NULL;
/*
- * Clear all the object caches.
+ * Clear all the caches.
*/
static void clear_caches(void)
{
- clear_cache(&cache_cachedname);
- clear_cache(&cache_class);
- clear_cache(&cache_codeblock);
- clear_cache(&cache_constructor);
- clear_cache(&cache_exception);
- clear_cache(&cache_ifacefile);
- clear_cache(&cache_mappedtype);
- clear_cache(&cache_member);
- clear_cache(&cache_module);
- clear_cache(&cache_qual);
- clear_cache(&cache_typehint);
- clear_cache(&cache_virtualerrorhandler);
- clear_cache(&cache_wrappedenum);
- clear_cache(&cache_wrappedtypedef);
+ clear_object_cache(&cache_cachedname);
+ clear_object_cache(&cache_class);
+ clear_object_cache(&cache_codeblock);
+ clear_object_cache(&cache_constructor);
+ clear_object_cache(&cache_exception);
+ clear_object_cache(&cache_ifacefile);
+ clear_object_cache(&cache_mappedtype);
+ clear_object_cache(&cache_member);
+ clear_object_cache(&cache_module);
+ clear_object_cache(&cache_qual);
+ clear_object_cache(&cache_virtualerrorhandler);
+ clear_object_cache(&cache_wrappedenum);
+ clear_object_cache(&cache_wrappedtypedef);
+ clear_str_cache(&cache_typehint);
}
/*
* Clear an object cache.
*/
-static void clear_cache(objectCache **head)
+static void clear_object_cache(objectCache **head)
{
objectCache *entry = *head;
@@ -138,9 +147,9 @@ static void clear_cache(objectCache **head)
/*
- * Add a Python object/C structure pair to a cache.
+ * Add a Python object/C structure pair to an object cache.
*/
-static void cache(objectCache **head, PyObject *py_obj, void *c_struct)
+static void cache_object(objectCache **head, PyObject *py_obj, void *c_struct)
{
objectCache *entry = sipMalloc(sizeof (objectCache));
@@ -153,10 +162,10 @@ static void cache(objectCache **head, PyObject *py_obj, void *c_struct)
/*
- * Search a cache for a Python object and return the already converted C
- * structure.
+ * Search an object cache for a Python object and return the already converted
+ * C structure.
*/
-static void *search_cache(const objectCache *cache, PyObject *py_obj)
+static void *search_object_cache(const objectCache *cache, PyObject *py_obj)
{
while (cache != NULL)
{
@@ -170,6 +179,60 @@ static void *search_cache(const objectCache *cache, PyObject *py_obj)
}
+/*
+ * Clear a str cache.
+ */
+static void clear_str_cache(strCache **head)
+{
+ strCache *entry = *head;
+
+ while (entry != NULL)
+ {
+ strCache *next = entry->next;
+
+ free(entry->c_struct);
+ free(entry);
+
+ entry = next;
+ }
+
+ *head = NULL;
+}
+
+
+/*
+ * Add a Python str/C structure pair to a str cache.
+ */
+static void cache_str(strCache **head, char *py_str, void *c_struct)
+{
+ strCache *entry = sipMalloc(sizeof (strCache));
+
+ entry->py_str = py_str;
+ entry->c_struct = c_struct;
+ entry->next = *head;
+
+ *head = entry;
+}
+
+
+/*
+ * Search a str cache for a Python string and return the already converted
+ * C structure.
+ */
+static void *search_str_cache(const strCache *cache, char *py_str)
+{
+ while (cache != NULL)
+ {
+ if (strcmp(cache->py_str, py_str) == 0)
+ return cache->c_struct;
+
+ cache = cache->next;
+ }
+
+ return NULL;
+}
+
+
/* Forward declarations of convertors. */
static argDef *argument(sipSpec *pt, PyObject *obj, const char *encoding);
static argDef *argument_attr(sipSpec *pt, PyObject *obj, const char *name,
@@ -190,8 +253,6 @@ static classDef *class_list_attr(sipSpec *pt, PyObject *obj, const char *name,
static classList *classlist_attr(sipSpec *pt, PyObject *obj, const char *name,
const char *encoding);
static codeBlock *codeblock(PyObject *obj, const char *encoding);
-static codeBlock *codeblock_attr(PyObject *obj, const char *name,
- const char *encoding);
static codeBlockList *codeblock_list_attr(PyObject *obj, const char *name,
const char *encoding);
static ctorDef *constructor(sipSpec *pt, PyObject *obj, const char *encoding);
@@ -227,8 +288,6 @@ static licenseDef *license_attr(PyObject *obj, const char *name,
const char *encoding);
static mappedTypeDef *mappedtype(sipSpec *pt, PyObject *obj,
const char *encoding);
-static mappedTypeDef *mappedtype_attr(sipSpec *pt, PyObject *obj,
- const char *name, const char *encoding);
static mappedTypeDef *mappedtype_list_attr(sipSpec *pt, PyObject *obj,
const char *name, const char *encoding);
static memberDef *member(sipSpec *pt, PyObject *obj, const char *encoding);
@@ -277,7 +336,6 @@ static throwArgs *throw_arguments(sipSpec *pt, PyObject *obj,
const char *encoding);
static throwArgs *throw_arguments_attr(sipSpec *pt, PyObject *obj,
const char *name, const char *encoding);
-static typeHintDef *typehint(PyObject *obj, const char *encoding);
static typeHintDef *typehint_attr(PyObject *obj, const char *name,
const char *encoding);
static void typehints_attr(PyObject *obj, const char *name,
@@ -326,7 +384,6 @@ static varDef *wrappedvariable_list_attr(sipSpec *pt, PyObject *obj,
/* Other forward declarations. */
static void appendCodeBlock(codeBlockList **headp, codeBlock *cb);
static scopedNameDef *text2scopePart(char *text);
-static typeHintDef *newTypeHint(char *raw_hint);
/*
@@ -586,12 +643,12 @@ static nameDef *cachedname(PyObject *obj, const char *encoding)
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_cachedname, obj)) != NULL)
+ if ((value = search_object_cache(cache_cachedname, obj)) != NULL)
return value;
value = sipMalloc(sizeof (nameDef));
- cache(&cache_cachedname, obj, value);
+ cache_object(&cache_cachedname, obj, value);
value->text = str_attr(obj, "name", encoding);
value->len = strlen(value->text);
@@ -666,12 +723,12 @@ static classDef *class(sipSpec *pt, PyObject *obj, const char *encoding)
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_class, obj)) != NULL)
+ if ((value = search_object_cache(cache_class, obj)) != NULL)
return value;
value = sipMalloc(sizeof (classDef));
- cache(&cache_class, obj, value);
+ cache_object(&cache_class, obj, value);
value->iff = ifacefile_attr(pt, obj, "iface_file", encoding);
value->docstring = docstring_attr(obj, "docstring", encoding);
@@ -895,12 +952,12 @@ static codeBlock *codeblock(PyObject *obj, const char *encoding)
{
codeBlock *value;
- if ((value = search_cache(cache_codeblock, obj)) != NULL)
+ if ((value = search_object_cache(cache_codeblock, obj)) != NULL)
return value;
value = sipMalloc(sizeof (codeBlock));
- cache(&cache_codeblock, obj, value);
+ cache_object(&cache_codeblock, obj, value);
value->frag = str_attr(obj, "text", encoding);
value->filename = str_attr(obj, "sip_file", encoding);
@@ -910,25 +967,6 @@ static codeBlock *codeblock(PyObject *obj, const char *encoding)
}
-/*
- * Convert a CodeBlock attribute.
- */
-static codeBlock *codeblock_attr(PyObject *obj, const char *name,
- const char *encoding)
-{
- PyObject *attr = PyObject_GetAttrString(obj, name);
- codeBlock *value;
-
- assert(attr != NULL);
-
- value = codeblock(attr, encoding);
-
- Py_DECREF(attr);
-
- return value;
-}
-
-
/*
* Convert a CodeBlock list or an optional CodeBlock attribute.
*/
@@ -973,12 +1011,12 @@ static ctorDef *constructor(sipSpec *pt, PyObject *obj, const char *encoding)
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_constructor, obj)) != NULL)
+ if ((value = search_object_cache(cache_constructor, obj)) != NULL)
return value;
value = sipMalloc(sizeof (ctorDef));
- cache(&cache_constructor, obj, value);
+ cache_object(&cache_constructor, obj, value);
value->docstring = docstring_attr(obj, "docstring", encoding);
@@ -1158,12 +1196,12 @@ static exceptionDef *exception(sipSpec *pt, PyObject *obj,
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_exception, obj)) != NULL)
+ if ((value = search_object_cache(cache_exception, obj)) != NULL)
return value;
value = sipMalloc(sizeof (exceptionDef));
- cache(&cache_exception, obj, value);
+ cache_object(&cache_exception, obj, value);
value->exceptionnr = int_attr(obj, "exception_nr");
value->iff = ifacefile_attr(pt, obj, "iface_file", encoding);
@@ -1262,12 +1300,12 @@ static ifaceFileDef *ifacefile(sipSpec *pt, PyObject *obj,
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_ifacefile, obj)) != NULL)
+ if ((value = search_object_cache(cache_ifacefile, obj)) != NULL)
return value;
value = sipMalloc(sizeof (ifaceFileDef));
- cache(&cache_ifacefile, obj, value);
+ cache_object(&cache_ifacefile, obj, value);
value->name = cachedname_attr(obj, "cpp_name", encoding);
value->needed = bool_attr(obj, "needed");
@@ -1419,12 +1457,12 @@ static mappedTypeDef *mappedtype(sipSpec *pt, PyObject *obj,
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_mappedtype, obj)) != NULL)
+ if ((value = search_object_cache(cache_mappedtype, obj)) != NULL)
return value;
value = sipMalloc(sizeof (mappedTypeDef));
- cache(&cache_mappedtype, obj, value);
+ cache_object(&cache_mappedtype, obj, value);
if (bool_attr(obj, "no_assignment_operator"))
setNoAssignOp(value);
@@ -1467,25 +1505,6 @@ static mappedTypeDef *mappedtype(sipSpec *pt, PyObject *obj,
}
-/*
- * Convert an optional MappedType attribute.
- */
-static mappedTypeDef *mappedtype_attr(sipSpec *pt, PyObject *obj,
- const char *name, const char *encoding)
-{
- PyObject *attr = PyObject_GetAttrString(obj, name);
- mappedTypeDef *value;
-
- assert(attr != NULL);
-
- value = mappedtype(pt, attr, encoding);
-
- Py_DECREF(attr);
-
- return value;
-}
-
-
/*
* Convert a MappedType list attribute.
*/
@@ -1523,12 +1542,12 @@ static memberDef *member(sipSpec *pt, PyObject *obj, const char *encoding)
memberDef *value;
int slot;
- if ((value = search_cache(cache_member, obj)) != NULL)
+ if ((value = search_object_cache(cache_member, obj)) != NULL)
return value;
value = sipMalloc(sizeof (memberDef));
- cache(&cache_member, obj, value);
+ cache_object(&cache_member, obj, value);
value->pyname = cachedname_attr(obj, "py_name", encoding);
@@ -1621,12 +1640,12 @@ static moduleDef *module(sipSpec *pt, PyObject *obj, const char *encoding)
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_module, obj)) != NULL)
+ if ((value = search_object_cache(cache_module, obj)) != NULL)
return value;
value = sipMalloc(sizeof (moduleDef));
- cache(&cache_module, obj, value);
+ cache_object(&cache_module, obj, value);
value->fullname = cachedname_attr(obj, "fq_py_name", encoding);
value->name = str_attr(obj, "py_name", encoding);
@@ -2049,12 +2068,12 @@ static qualDef *qual(sipSpec *pt, PyObject *obj, const char *encoding)
{
qualDef *value;
- if ((value = search_cache(cache_qual, obj)) != NULL)
+ if ((value = search_object_cache(cache_qual, obj)) != NULL)
return value;
value = sipMalloc(sizeof (qualDef));
- cache(&cache_qual, obj, value);
+ cache_object(&cache_qual, obj, value);
value->name = str_attr(obj, "name", encoding);
value->qtype = (qualType)enum_attr(obj, "type");
@@ -2400,38 +2419,33 @@ static throwArgs *throw_arguments_attr(sipSpec *pt, PyObject *obj,
/*
- * Convert an optional TypeHint object.
- */
-static typeHintDef *typehint(PyObject *obj, const char *encoding)
-{
- typeHintDef *value;
-
- if (obj == Py_None)
- return NULL;
-
- if ((value = search_cache(cache_typehint, obj)) != NULL)
- return value;
-
- value = newTypeHint(str_attr(obj, "text", encoding));
-
- cache(&cache_typehint, obj, value);
-
- return value;
-}
-
-
-/*
- * Convert an optional TypeHint attribute.
+ * Convert an optional str attribute as a typeHintDef.
*/
static typeHintDef *typehint_attr(PyObject *obj, const char *name,
const char *encoding)
{
PyObject *attr = PyObject_GetAttrString(obj, name);
typeHintDef *value;
+ char *raw_hint;
assert(attr != NULL);
- value = typehint(attr, encoding);
+ if ((raw_hint = str(attr, encoding)) != NULL)
+ {
+ if ((value = search_str_cache(cache_typehint, raw_hint)) == NULL)
+ {
+ value = sipMalloc(sizeof (typeHintDef));
+
+ cache_str(&cache_typehint, raw_hint, value);
+
+ value->status = needs_parsing;
+ value->raw_hint = raw_hint;
+ }
+ }
+ else
+ {
+ value = NULL;
+ }
Py_DECREF(attr);
@@ -2587,12 +2601,12 @@ static virtErrorHandler *virtualerrorhandler(sipSpec *pt, PyObject *obj,
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_virtualerrorhandler, obj)) != NULL)
+ if ((value = search_object_cache(cache_virtualerrorhandler, obj)) != NULL)
return value;
value = sipMalloc(sizeof (virtErrorHandler));
- cache(&cache_virtualerrorhandler, obj, value);
+ cache_object(&cache_virtualerrorhandler, obj, value);
value->name = str_attr(obj, "name", encoding);
value->code = codeblock_list_attr(obj, "code", encoding);
@@ -2825,12 +2839,12 @@ static enumDef *wrappedenum(sipSpec *pt, PyObject *obj, const char *encoding)
int base_type;
PyObject *scope_obj;
- if ((value = search_cache(cache_wrappedenum, obj)) != NULL)
+ if ((value = search_object_cache(cache_wrappedenum, obj)) != NULL)
return value;
value = sipMalloc(sizeof (enumDef));
- cache(&cache_wrappedenum, obj, value);
+ cache_object(&cache_wrappedenum, obj, value);
if (bool_attr(obj, "is_protected"))
setIsProtectedEnum(value);
@@ -3004,12 +3018,12 @@ static typedefDef *wrappedtypedef(sipSpec *pt, PyObject *obj,
if (obj == Py_None)
return NULL;
- if ((value = search_cache(cache_wrappedtypedef, obj)) != NULL)
+ if ((value = search_object_cache(cache_wrappedtypedef, obj)) != NULL)
return value;
value = sipMalloc(sizeof (typedefDef));
- cache(&cache_wrappedtypedef, obj, value);
+ cache_object(&cache_wrappedtypedef, obj, value);
if (bool_attr(obj, "no_type_name"))
setNoTypeName(value);
@@ -3172,17 +3186,3 @@ static scopedNameDef *text2scopePart(char *text)
return snd;
}
-
-
-/*
- * Create a new type hint for a raw string.
- */
-static typeHintDef *newTypeHint(char *raw_hint)
-{
- typeHintDef *thd = sipMalloc(sizeof (typeHintDef));
-
- thd->status = needs_parsing;
- thd->raw_hint = raw_hint;
-
- return thd;
-}
diff --git a/code_generator/pybinding.c b/code_generator/pybinding.c
index c4745b9..28c786c 100644
--- a/code_generator/pybinding.c
+++ b/code_generator/pybinding.c
@@ -46,8 +46,6 @@ static void raise_exception(void);
static PyObject *py_set_globals(PyObject *self, PyObject *args);
static PyObject *py_py2c(PyObject *self, PyObject *args);
static PyObject *py_generateCode(PyObject *self, PyObject *args);
-static PyObject *py_generateXML(PyObject *self, PyObject *args);
-static PyObject *py_generateTypeHints(PyObject *self, PyObject *args);
static int fs_convertor(PyObject *obj, char **fsp);
static int sipSpec_convertor(PyObject *obj, sipSpec **ptp);
@@ -65,8 +63,6 @@ PyMODINIT_FUNC PyInit_code_generator(void)
{"set_globals", py_set_globals, METH_VARARGS, NULL},
{"py2c", py_py2c, METH_VARARGS, NULL},
{"generateCode", py_generateCode, METH_VARARGS, NULL},
- {"generateXML", py_generateXML, METH_VARARGS, NULL},
- {"generateTypeHints", py_generateTypeHints, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL},
};
@@ -170,52 +166,6 @@ static PyObject *py_generateCode(PyObject *self, PyObject *args)
}
-/*
- * Wrapper around generateXML().
- */
-static PyObject *py_generateXML(PyObject *self, PyObject *args)
-{
- sipSpec *pt;
- char *xmlFile;
-
- if (!PyArg_ParseTuple(args, "O&O&",
- sipSpec_convertor, &pt,
- fs_convertor, &xmlFile))
- return NULL;
-
- if (generateXML(pt, pt->module, xmlFile) < 0)
- {
- raise_exception();
- return NULL;
- }
-
- Py_RETURN_NONE;
-}
-
-
-/*
- * Wrapper around generateTypeHints().
- */
-static PyObject *py_generateTypeHints(PyObject *self, PyObject *args)
-{
- sipSpec *pt;
- char *pyiFile;
-
- if (!PyArg_ParseTuple(args, "O&O&",
- sipSpec_convertor, &pt,
- fs_convertor, &pyiFile))
- return NULL;
-
- if (generateTypeHints(pt, pt->module, pyiFile) < 0)
- {
- raise_exception();
- return NULL;
- }
-
- Py_RETURN_NONE;
-}
-
-
/*
* Convert a callable argument to a filesystem name.
*/
diff --git a/code_generator/sip.h b/code_generator/sip.h
index a458be8..31fcbb0 100644
--- a/code_generator/sip.h
+++ b/code_generator/sip.h
@@ -1298,8 +1298,6 @@ void get_bindings_configuration(const char *sip_file, stringList **tags,
stringList *generateCode(sipSpec *, char *, const char *, int, int, int, int,
stringList *needed_qualifiers, stringList *, int, int,
const char **api_header);
-int generateXML(sipSpec *pt, moduleDef *mod, const char *xmlFile);
-int generateTypeHints(sipSpec *pt, moduleDef *mod, const char *pyiFile);
void generateExpression(valueDef *vd, int in_str, FILE *fp);
int error(const char *fmt, ...);
void errorAppend(const char *fmt, ...);
@@ -1312,23 +1310,19 @@ int compareScopedNames(scopedNameDef *snd1, scopedNameDef *snd2);
char *scopedNameTail(scopedNameDef *);
void prcode(FILE *fp, const char *fmt, ...);
void prCopying(FILE *fp, moduleDef *mod, const char *comment);
-void prDefaultValue(argDef *ad, int in_str, FILE *fp);
+void prDefaultValue(argDef *ad, FILE *fp);
void prScopedPythonName(FILE *fp, classDef *scope, const char *pyname);
int isNumberSlot(memberDef *md);
void appendString(stringList **headp, const char *s);
int pluginPyQt5(sipSpec *pt);
int pluginPyQt6(sipSpec *pt);
memberDef *findMethod(classDef *cd, const char *name);
-void pyiCtor(sipSpec *pt, moduleDef *mod, classDef *cd, ctorDef *ct,
- int overloaded, ifaceFileList *defined, int indent, FILE *fp);
-void pyiOverload(sipSpec *pt, moduleDef *mod, overDef *od, int overloaded,
- int is_method, ifaceFileList *defined, int indent, int pep484,
+void pyiCtor(sipSpec *pt, moduleDef *mod, classDef *cd, ctorDef *ct, FILE *fp);
+void pyiOverload(sipSpec *pt, moduleDef *mod, overDef *od, int is_method,
FILE *fp);
scopedNameDef *removeGlobalScope(scopedNameDef *snd);
void pyiTypeHint(sipSpec *pt, typeHintDef *thd, moduleDef *mod, int out,
- ifaceFileList *defined, int pep484, int rest, FILE *fp);
-void restPyClass(classDef *cd, FILE *fp);
-void restPyEnum(enumDef *ed, FILE *fp);
+ FILE *fp);
void generateBaseType(ifaceFileDef *scope, argDef *ad, int use_typename,
int strip, FILE *fp);
void normaliseArgs(signatureDef *sd);
diff --git a/code_generator/type_hints.c b/code_generator/type_hints.c
index 583b48a..0b813cb 100644
--- a/code_generator/type_hints.c
+++ b/code_generator/type_hints.c
@@ -23,47 +23,17 @@
#include "sip.h"
-/* Return a string referring to an object of any type. */
-#define anyObject(pep484) ((pep484) ? "typing.Any" : "object")
-
-
-static void pyiCompositeModule(sipSpec *pt, moduleDef *comp_mod, FILE *fp);
-static void pyiModule(sipSpec *pt, moduleDef *mod, FILE *fp);
-static void pyiTypeHintCode(codeBlockList *thc, int indent, FILE *fp);
-static void pyiEnums(sipSpec *pt, moduleDef *mod, ifaceFileDef *scope,
- ifaceFileList *defined, int indent, FILE *fp);
-static void pyiVars(sipSpec *pt, moduleDef *mod, classDef *scope,
- ifaceFileList *defined, int indent, FILE *fp);
-static void pyiClass(sipSpec *pt, moduleDef *mod, classDef *cd,
- ifaceFileList **defined, int indent, FILE *fp);
-static void pyiMappedType(sipSpec *pt, moduleDef *mod, mappedTypeDef *mtd,
- ifaceFileList **defined, int indent, FILE *fp);
-static void pyiCallable(sipSpec *pt, moduleDef *mod, memberDef *md,
- overDef *overloads, int is_method, ifaceFileList *defined, int indent,
- FILE *fp);
-static void pyiProperty(sipSpec *pt, moduleDef *mod, propertyDef *pd,
- int is_setter, memberDef *md, overDef *overloads,
- ifaceFileList *defined, int indent, FILE *fp);
static void pyiPythonSignature(sipSpec *pt, moduleDef *mod, signatureDef *sd,
- int need_self, ifaceFileList *defined, KwArgs kwargs, int pep484,
- FILE *fp);
+ int need_self, KwArgs kwargs, FILE *fp);
static int pyiArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int arg_nr,
- int out, int need_comma, int names, int defaults,
- ifaceFileList *defined, KwArgs kwargs, int pep484, FILE *fp);
+ int out, int need_comma, int names, int defaults, KwArgs kwargs,
+ FILE *fp);
static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
- ifaceFileList *defined, int pep484, FILE *fp);
-static void pyiTypeHintNode(typeHintNodeDef *node, moduleDef *mod,
- ifaceFileList *defined, int pep484, int rest, FILE *fp);
-static void prIndent(int indent, FILE *fp);
-static int separate(int first, int indent, FILE *fp);
-static void prClassRef(classDef *cd, moduleDef *mod, ifaceFileList *defined,
- int pep484, FILE *fp);
-static void prEnumRef(enumDef *ed, moduleDef *mod, ifaceFileList *defined,
- int pep484, FILE *fp);
+ FILE *fp);
+static void pyiTypeHintNode(typeHintNodeDef *node, moduleDef *mod, FILE *fp);
+static void prClassRef(classDef *cd, FILE *fp);
+static void prEnumRef(enumDef *ed, FILE *fp);
static void prScopedEnumName(FILE *fp, enumDef *ed);
-static int isDefined(ifaceFileDef *iff, classDef *cd, moduleDef *mod,
- ifaceFileList *defined);
-static int inIfaceFileList(ifaceFileDef *iff, ifaceFileList *defined);
static void parseTypeHint(sipSpec *pt, typeHintDef *thd, int out);
static int parseTypeHintNode(sipSpec *pt, int out, int top_level, char *start,
char *end, typeHintNodeDef **thnp);
@@ -74,738 +44,43 @@ static enumDef *lookupEnum(sipSpec *pt, const char *name, classDef *scope_cd,
static mappedTypeDef *lookupMappedType(sipSpec *pt, const char *name);
static classDef *lookupClass(sipSpec *pt, const char *name,
classDef *scope_cd);
-static void maybeAnyObject(const char *hint, int pep484, FILE *fp);
+static void maybeAnyObject(const char *hint, FILE *fp);
static void strip_leading(char **startp, char *end);
static void strip_trailing(char *start, char **endp);
static typeHintNodeDef *flatten_unions(typeHintNodeDef *nodes);
static typeHintNodeDef *copyTypeHintNode(sipSpec *pt, typeHintDef *thd,
int out);
static int isPyKeyword(const char *word);
-static void appendToIfaceFileList(ifaceFileList **ifflp, ifaceFileDef *iff);
-
-
-/*
- * Generate the .pyi file.
- */
-int generateTypeHints(sipSpec *pt, moduleDef *mod, const char *pyiFile)
-{
- FILE *fp;
-
- /* Generate the file. */
- if ((fp = fopen(pyiFile, "w")) == NULL)
- return error("Unable to create file \"%s\"\n", pyiFile);
-
- /* Write the header. */
- fprintf(fp,
-"# The PEP 484 type hints stub file for the %s module.\n"
- , mod->name);
-
- if (sipVersionStr != NULL)
- fprintf(fp,
-"#\n"
-"# Generated by SIP %s\n"
- , sipVersionStr);
-
- prCopying(fp, mod, "#");
-
- fprintf(fp,
-"\n"
-"\n"
- );
-
- if (isComposite(mod))
- pyiCompositeModule(pt, mod, fp);
- else
- pyiModule(pt, mod, fp);
-
- fclose(fp);
-
- return 0;
-}
-
-
-/*
- * Generate the type hints for a composite module.
- */
-static void pyiCompositeModule(sipSpec *pt, moduleDef *comp_mod, FILE *fp)
-{
- moduleDef *mod;
-
- for (mod = pt->modules; mod != NULL; mod = mod->next)
- if (mod->container == comp_mod)
- fprintf(fp, "from %s import *\n", mod->fullname->text);
-}
-
-
-/*
- * Generate the type hints for an ordinary module.
- */
-static void pyiModule(sipSpec *pt, moduleDef *mod, FILE *fp)
-{
- char *cp;
- int first;
- memberDef *md;
- classDef *cd;
- mappedTypeDef *mtd;
- ifaceFileList *defined;
- moduleListDef *mld;
-
- /*
- * Generate the imports. Note that we assume the super-types are the
- * standard SIP ones.
- */
- if (abiVersion >= ABI_13_0)
- fprintf(fp,
-"import enum\n"
- );
-
- fprintf(fp,
-"import typing\n"
-"\n"
-"import %s\n"
- , (sipName != NULL) ? sipName : "sip");
-
- first = TRUE;
-
- for (mld = mod->imports; mld != NULL; mld = mld->next)
- {
- /* We lie about the indent because we only want one blank line. */
- first = separate(first, 1, fp);
-
- if ((cp = strrchr(mld->module->fullname->text, '.')) == NULL)
- {
- fprintf(fp, "import %s\n", mld->module->name);
- }
- else
- {
- *cp = '\0';
- fprintf(fp, "from %s import %s\n", mld->module->fullname->text,
- mld->module->name);
- *cp = '.';
- }
- }
-
- /*
- * Generate any exported type hint code and any module-specific type hint
- * code.
- */
- pyiTypeHintCode(pt->exptypehintcode, 0, fp);
- pyiTypeHintCode(mod->typehintcode, 0, fp);
-
- /* Generate the types - global enums must be first. */
- pyiEnums(pt, mod, NULL, NULL, 0, fp);
-
- defined = NULL;
-
- for (cd = pt->classes; cd != NULL; cd = cd->next)
- {
- if (cd->iff->module != mod)
- continue;
-
- if (isExternal(cd))
- continue;
-
- if (cd->no_typehint)
- continue;
-
- /* Only handle non-nested classes here. */
- if (cd->ecd != NULL)
- continue;
-
- /* We can't handle extenders. */
- if (cd->real != NULL)
- continue;
-
- pyiClass(pt, mod, cd, &defined, 0, fp);
- }
-
- for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next)
- {
- if (mtd->iff->module != mod)
- continue;
-
- if (mtd->pyname != NULL)
- pyiMappedType(pt, mod, mtd, &defined, 0, fp);
- }
-
- pyiVars(pt, mod, NULL, defined, 0, fp);
-
- first = TRUE;
-
- for (md = mod->othfuncs; md != NULL; md = md->next)
- if (md->slot == no_slot)
- {
- first = separate(first, 0, fp);
-
- pyiCallable(pt, mod, md, mod->overs, FALSE, defined, 0, fp);
- }
-}
-
-
-/*
- * Generate handwritten type hint code.
- */
-static void pyiTypeHintCode(codeBlockList *thc, int indent, FILE *fp)
-{
- while (thc != NULL)
- {
- int need_indent = TRUE;
- const char *cp;
-
- fprintf(fp, "\n");
-
- for (cp = thc->block->frag; *cp != '\0'; ++cp)
- {
- if (need_indent)
- {
- need_indent = FALSE;
- prIndent(indent, fp);
- }
-
- fprintf(fp, "%c", *cp);
-
- if (*cp == '\n')
- need_indent = TRUE;
- }
-
- thc = thc->next;
- }
-}
-
-
-/*
- * Generate the type hints for a class.
- */
-static void pyiClass(sipSpec *pt, moduleDef *mod, classDef *cd,
- ifaceFileList **defined, int indent, FILE *fp)
-{
- int first, no_body, nr_overloads;
- classDef *nested;
- ctorDef *ct;
- memberDef *md;
- propertyDef *pd;
-
- nr_overloads = 0;
-
- if (!isHiddenNamespace(cd))
- {
- separate(TRUE, indent, fp);
- prIndent(indent, fp);
- fprintf(fp, "class %s(", cd->pyname->text);
-
- if (cd->supers != NULL)
- {
- classList *cl;
-
- for (cl = cd->supers; cl != NULL; cl = cl->next)
- {
- if (cl != cd->supers)
- fprintf(fp, ", ");
-
- prClassRef(cl->cd, mod, *defined, TRUE, fp);
- }
- }
- else if (cd->supertype != NULL)
- {
- /*
- * This is a hack to correct the .pyi file when the supertype
- * hasn't been given a fully qualified name (ie. with ABI v12).
- */
- if (sipName != NULL && strncmp(cd->supertype->text, "sip.", 4) == 0)
- fprintf(fp, "%s.%s", sipName, &cd->supertype->text[4]);
- else
- fprintf(fp, "%s", cd->supertype->text);
- }
- else
- {
- fprintf(fp, "%s.%swrapper", (sipName != NULL ? sipName : "sip"),
- (cd->iff->type == namespace_iface ? "simple" : ""));
- }
-
- /* See if there is anything in the class body. */
- for (ct = cd->ctors; ct != NULL; ct = ct->next)
- {
- if (isPrivateCtor(ct))
- continue;
-
- if (ct->no_typehint)
- continue;
-
- ++nr_overloads;
- }
-
- no_body = (cd->typehintcode == NULL && nr_overloads == 0);
-
- if (no_body)
- {
- overDef *od;
-
- for (od = cd->overs; od != NULL; od = od->next)
- {
- if (isPrivate(od))
- continue;
-
- if (od->no_typehint)
- continue;
-
- no_body = FALSE;
- break;
- }
- }
-
- if (no_body)
- {
- enumDef *ed;
-
- for (ed = pt->enums; ed != NULL; ed = ed->next)
- {
- if (ed->no_typehint)
- continue;
-
- if (ed->ecd == cd)
- {
- no_body = FALSE;
- break;
- }
- }
- }
-
- if (no_body)
- {
- for (nested = pt->classes; nested != NULL; nested = nested->next)
- {
- if (nested->no_typehint)
- continue;
-
- if (nested->ecd == cd)
- {
- no_body = FALSE;
- break;
- }
- }
- }
-
- if (no_body)
- {
- varDef *vd;
-
- for (vd = pt->vars; vd != NULL; vd = vd->next)
- {
- if (vd->no_typehint)
- continue;
-
- if (vd->ecd == cd)
- {
- no_body = FALSE;
- break;
- }
- }
- }
-
- fprintf(fp, "):%s\n", (no_body ? " ..." : ""));
-
- ++indent;
-
- pyiTypeHintCode(cd->typehintcode, indent, fp);
- }
-
- pyiEnums(pt, mod, cd->iff, *defined, indent, fp);
-
- /* Handle any nested classes. */
- for (nested = pt->classes; nested != NULL; nested = nested->next)
- if (nested->ecd == cd && !nested->no_typehint)
- pyiClass(pt, mod, nested, defined, indent, fp);
-
- pyiVars(pt, mod, cd, *defined, indent, fp);
-
- first = TRUE;
-
- for (ct = cd->ctors; ct != NULL; ct = ct->next)
- {
- int overloaded;
-
- if (isPrivateCtor(ct))
- continue;
-
- if (ct->no_typehint)
- continue;
-
- overloaded = (nr_overloads > 1);
-
- first = separate(first, indent, fp);
-
- pyiCtor(pt, mod, NULL, ct, overloaded, *defined, indent, fp);
- }
-
- first = TRUE;
-
- for (md = cd->members; md != NULL; md = md->next)
- {
- first = separate(first, indent, fp);
-
- pyiCallable(pt, mod, md, cd->overs, !isHiddenNamespace(cd), *defined,
- indent, fp);
- }
-
- for (pd = cd->properties; pd != NULL; pd = pd->next)
- {
- first = separate(first, indent, fp);
-
- if (pd->get != NULL)
- {
- if ((md = findMethod(cd, pd->get)) != NULL)
- {
- pyiProperty(pt, mod, pd, FALSE, md, cd->overs, *defined,
- indent, fp);
-
- if (pd->set != NULL)
- {
- if ((md = findMethod(cd, pd->set)) != NULL)
- pyiProperty(pt, mod, pd, TRUE, md, cd->overs, *defined,
- indent, fp);
- }
- }
- }
- }
-
- if (!isHiddenNamespace(cd))
- {
- /*
- * Keep track of what has been defined so that forward references are
- * no longer required.
- */
- appendToIfaceFileList(defined, cd->iff);
- }
-}
-
-
-/*
- * Generate the type hints for a mapped type.
- */
-static void pyiMappedType(sipSpec *pt, moduleDef *mod, mappedTypeDef *mtd,
- ifaceFileList **defined, int indent, FILE *fp)
-{
- int first, no_body;
- memberDef *md;
-
- /* See if there is anything in the mapped type body. */
- no_body = (mtd->members == NULL);
-
- if (no_body)
- {
- enumDef *ed;
-
- for (ed = pt->enums; ed != NULL; ed = ed->next)
- {
- if (ed->no_typehint)
- continue;
-
- if (ed->emtd == mtd)
- {
- no_body = FALSE;
- break;
- }
- }
- }
-
- if (!no_body)
- {
- separate(TRUE, indent, fp);
- prIndent(indent, fp);
- fprintf(fp, "class %s(%s.wrapper):\n", mtd->pyname->text, (sipName != NULL ? sipName : "sip"));
-
- ++indent;
-
- pyiEnums(pt, mod, mtd->iff, *defined, indent, fp);
-
- first = TRUE;
-
- for (md = mtd->members; md != NULL; md = md->next)
- {
- first = separate(first, indent, fp);
-
- pyiCallable(pt, mod, md, mtd->overs, TRUE, *defined, indent, fp);
- }
- }
-
- /*
- * Keep track of what has been defined so that forward references are no
- * longer required.
- */
- appendToIfaceFileList(defined, mtd->iff);
-}
/*
* Generate an ctor type hint.
*/
-void pyiCtor(sipSpec *pt, moduleDef *mod, classDef *cd, ctorDef *ct,
- int overloaded, ifaceFileList *defined, int indent, FILE *fp)
+void pyiCtor(sipSpec *pt, moduleDef *mod, classDef *cd, ctorDef *ct, FILE *fp)
{
- int a, need_comma;
-
- if (overloaded)
- {
- prIndent(indent, fp);
- fprintf(fp, "@typing.overload\n");
- }
+ int a, need_comma = FALSE;
- prIndent(indent, fp);
-
- if (cd == NULL)
- {
- fprintf(fp, "def __init__(self");
- need_comma = TRUE;
- }
- else
- {
- prScopedPythonName(fp, cd->ecd, cd->pyname->text);
- fprintf(fp, "(");
- need_comma = FALSE;
- }
+ prScopedPythonName(fp, cd->ecd, cd->pyname->text);
+ fprintf(fp, "(");
for (a = 0; a < ct->pysig.nrArgs; ++a)
need_comma = pyiArgument(pt, mod, &ct->pysig.args[a], a, FALSE,
- need_comma, TRUE, TRUE, defined, ct->kwargs, (cd == NULL),
- fp);
-
- fprintf(fp, (cd == NULL) ? ") -> None: ...\n" : ")");
-}
-
-
-/*
- * Generate the APIs for all the enums in a scope.
- */
-static void pyiEnums(sipSpec *pt, moduleDef *mod, ifaceFileDef *scope,
- ifaceFileList *defined, int indent, FILE *fp)
-{
- enumDef *ed;
-
- for (ed = pt->enums; ed != NULL; ed = ed->next)
- {
- enumMemberDef *emd;
-
- if (ed->module != mod)
- continue;
-
- if (ed->no_typehint)
- continue;
-
- if (scope != NULL)
- {
- if ((ed->ecd == NULL || ed->ecd->iff != scope) && (ed->emtd == NULL || ed->emtd->iff != scope))
- continue;
- }
- else if (ed->ecd != NULL || ed->emtd != NULL)
- {
- continue;
- }
-
- separate(TRUE, indent, fp);
-
- if (ed->pyname != NULL)
- {
- const char *super = "int";
-
- prIndent(indent, fp);
-
- if (abiVersion >= ABI_13_0)
- {
- if (isEnumEnum(ed))
- super = "enum.Enum";
- else if (isEnumFlag(ed))
- super = "enum.Flag";
- else if (isEnumIntEnum(ed) || isEnumUIntEnum(ed))
- super = "enum.IntEnum";
- else if (isEnumIntFlag(ed))
- super = "enum.IntFlag";
- }
-
- fprintf(fp, "class %s(%s):\n", ed->pyname->text, super);
-
- ++indent;
- }
-
- for (emd = ed->members; emd != NULL; emd = emd->next)
- {
- if (emd->no_typehint)
- continue;
-
- prIndent(indent, fp);
- fprintf(fp, "%s = ... # type: ", emd->pyname->text);
-
- if (ed->pyname != NULL)
- prScopedPythonName(fp, ed->ecd, ed->pyname->text);
- else
- fprintf(fp, "int");
-
- fprintf(fp, "\n");
- }
-
- if (ed->pyname != NULL)
- --indent;
- }
-}
-
-
-/*
- * Generate the APIs for all the variables in a scope.
- */
-static void pyiVars(sipSpec *pt, moduleDef *mod, classDef *scope,
- ifaceFileList *defined, int indent, FILE *fp)
-{
- int first = TRUE;
- varDef *vd;
-
- for (vd = pt->vars; vd != NULL; vd = vd->next)
- {
- if (vd->module != mod)
- continue;
-
- if (vd->ecd != scope)
- continue;
-
- if (vd->no_typehint)
- continue;
-
- first = separate(first, indent, fp);
-
- prIndent(indent, fp);
- fprintf(fp, "%s = ... # type: ", vd->pyname->text);
- pyiType(pt, mod, &vd->type, FALSE, defined, TRUE, fp);
- fprintf(fp, "\n");
- }
-}
+ need_comma, TRUE, TRUE, ct->kwargs, fp);
-
-/*
- * Generate the type hints for a callable.
- */
-static void pyiCallable(sipSpec *pt, moduleDef *mod, memberDef *md,
- overDef *overloads, int is_method, ifaceFileList *defined, int indent,
- FILE *fp)
-{
- int nr_overloads;
- overDef *od;
-
- /* Count the number of overloads. */
- nr_overloads = 0;
-
- for (od = overloads; od != NULL; od = od->next)
- {
- if (isPrivate(od))
- continue;
-
- if (od->common != md)
- continue;
-
- if (od->no_typehint)
- continue;
-
- ++nr_overloads;
- }
-
- /* Handle each overload. */
- for (od = overloads; od != NULL; od = od->next)
- {
- int overloaded;
-
- if (isPrivate(od))
- continue;
-
- if (od->common != md)
- continue;
-
- if (od->no_typehint)
- continue;
-
- overloaded = (nr_overloads > 1);
-
- pyiOverload(pt, mod, od, overloaded, is_method, defined, indent, TRUE,
- fp);
- }
-}
-
-
-/*
- * Generate the type hints for a property.
- */
-static void pyiProperty(sipSpec *pt, moduleDef *mod, propertyDef *pd,
- int is_setter, memberDef *md, overDef *overloads,
- ifaceFileList *defined, int indent, FILE *fp)
-{
- overDef *od;
-
- /* Handle each overload. */
- for (od = overloads; od != NULL; od = od->next)
- {
- if (isPrivate(od))
- continue;
-
- if (od->common != md)
- continue;
-
- if (od->no_typehint)
- continue;
-
- prIndent(indent, fp);
-
- if (is_setter)
- fprintf(fp, "@%s.setter\n", pd->name->text);
- else
- fprintf(fp, "@property\n");
-
- prIndent(indent, fp);
-
- fprintf(fp, "def %s", pd->name->text);
-
- pyiPythonSignature(pt, mod, &od->pysig, TRUE, defined, od->kwargs,
- TRUE, fp);
-
- fprintf(fp, ": ...\n");
-
- break;
- }
+ fprintf(fp, ")");
}
/*
* Generate the type hints for a single API overload.
*/
-void pyiOverload(sipSpec *pt, moduleDef *mod, overDef *od, int overloaded,
- int is_method, ifaceFileList *defined, int indent, int pep484,
+void pyiOverload(sipSpec *pt, moduleDef *mod, overDef *od, int is_method,
FILE *fp)
{
- if (overloaded)
- {
- prIndent(indent, fp);
- fprintf(fp, "@typing.overload\n");
- }
-
- if (pep484 && is_method && isStatic(od))
- {
- prIndent(indent, fp);
- fprintf(fp, "@staticmethod\n");
- }
-
- prIndent(indent, fp);
- fprintf(fp, "%s%s", (pep484 ? "def " : ""), od->common->pyname->text);
-
- if (pep484 && (od->common->slot == eq_slot || od->common->slot == ne_slot))
- {
- /* mypy recommends using 'object' as the argument type. */
- fprintf(fp, "(self, other: object)");
- }
- else
- {
- int need_self = (is_method && !isStatic(od));
-
- pyiPythonSignature(pt, mod, &od->pysig, need_self, defined, od->kwargs,
- pep484, fp);
- }
+ int need_self = (is_method && !isStatic(od));
- if (pep484)
- fprintf(fp, ": ...\n");
+ fprintf(fp, "%s", od->common->pyname->text);
+ pyiPythonSignature(pt, mod, &od->pysig, need_self, od->kwargs, fp);
}
@@ -813,8 +88,8 @@ void pyiOverload(sipSpec *pt, moduleDef *mod, overDef *od, int overloaded,
* Generate a Python argument.
*/
static int pyiArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int arg_nr,
- int out, int need_comma, int names, int defaults,
- ifaceFileList *defined, KwArgs kwargs, int pep484, FILE *fp)
+ int out, int need_comma, int names, int defaults, KwArgs kwargs,
+ FILE *fp)
{
int optional, use_optional;
@@ -826,13 +101,6 @@ static int pyiArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int arg_nr,
optional = (defaults && ad->defval && !out);
- /*
- * We only show names for PEP 484 type hints and when they are part of the
- * API.
- */
- if (names)
- names = (pep484 || kwargs == AllKwArgs || (kwargs == OptionalKwArgs && optional));
-
if (names && ad->atype != ellipsis_type)
{
if (ad->name != NULL)
@@ -844,7 +112,7 @@ static int pyiArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int arg_nr,
use_optional = FALSE;
- if (optional && pep484)
+ if (optional)
{
/* Assume pointers can be None unless specified otherwise. */
if (isAllowNone(ad) || (!isDisallowNone(ad) && ad->nrderefs > 0))
@@ -857,7 +125,7 @@ static int pyiArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int arg_nr,
if (isArray(ad))
fprintf(fp, "%s.array[", (sipName != NULL) ? sipName : "sip");
- pyiType(pt, mod, ad, out, defined, pep484, fp);
+ pyiType(pt, mod, ad, out, fp);
if (names && ad->atype == ellipsis_type)
{
@@ -878,10 +146,7 @@ static int pyiArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int arg_nr,
fprintf(fp, " = ");
- if (pep484)
- fprintf(fp, "...");
- else
- prDefaultValue(ad, TRUE, fp);
+ prDefaultValue(ad, fp);
}
return TRUE;
@@ -891,7 +156,7 @@ static int pyiArgument(sipSpec *pt, moduleDef *mod, argDef *ad, int arg_nr,
/*
* Generate the default value of an argument.
*/
-void prDefaultValue(argDef *ad, int in_str, FILE *fp)
+void prDefaultValue(argDef *ad, FILE *fp)
{
/* Use any explicitly provided documentation. */
if (ad->typehint_value != NULL)
@@ -918,7 +183,7 @@ void prDefaultValue(argDef *ad, int in_str, FILE *fp)
/* SIP v5 won't need this. */
prcode(fp, "%M");
- generateExpression(ad->defval, in_str, fp);
+ generateExpression(ad->defval, TRUE, fp);
prcode(fp, "%M");
}
@@ -926,8 +191,7 @@ void prDefaultValue(argDef *ad, int in_str, FILE *fp)
/*
* Generate the Python representation of a type.
*/
-static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
- ifaceFileList *defined, int pep484, FILE *fp)
+static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out, FILE *fp)
{
const char *type_name, *sip_name;
typeHintDef *thd;
@@ -937,7 +201,7 @@ static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
if (thd != NULL)
{
- pyiTypeHint(pt, thd, mod, out, defined, pep484, FALSE, fp);
+ pyiTypeHint(pt, thd, mod, out, fp);
return;
}
@@ -948,7 +212,7 @@ static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
switch (ad->atype)
{
case class_type:
- prClassRef(ad->u.cd, mod, defined, pep484, fp);
+ prClassRef(ad->u.cd, fp);
break;
case mapped_type:
@@ -956,12 +220,12 @@ static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
* This should never happen as it should have been picked up when
* generating code - but maybe we haven't been asked to generate code.
*/
- fprintf(fp, anyObject(pep484));
+ fprintf(fp, "object");
break;
case enum_type:
if (ad->u.ed->pyname != NULL)
- prEnumRef(ad->u.ed, mod, defined, pep484, fp);
+ prEnumRef(ad->u.ed, fp);
else
type_name = "int";
@@ -1020,23 +284,23 @@ static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
break;
case pyobject_type:
- type_name = anyObject(pep484);
+ type_name = "object";
break;
case pytuple_type:
- type_name = (pep484 ? "typing.Tuple" : "Tuple");
+ type_name = "Tuple";
break;
case pylist_type:
- type_name = (pep484 ? "typing.List" : "List");
+ type_name = "List";
break;
case pydict_type:
- type_name = (pep484 ? "typing.Dict" : "Dict");
+ type_name = "Dict";
break;
case pycallable_type:
- type_name = (pep484 ? "typing.Callable[..., None]" : "Callable[..., None]");
+ type_name = "Callable[..., None]";
break;
case pyslice_type:
@@ -1048,12 +312,8 @@ static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
break;
case pybuffer_type:
- if (pep484)
- fprintf(fp, "%s.Buffer", sip_name);
- else
- /* This replicates sip.pyi. */
- fprintf(fp, "Union[bytes, bytearray, memoryview, %s.array, %s.voidptr]", sip_name, sip_name);
-
+ /* This replicates sip.pyi. */
+ fprintf(fp, "Union[bytes, bytearray, memoryview, %s.array, %s.voidptr]", sip_name, sip_name);
break;
case pyenum_type:
@@ -1065,7 +325,7 @@ static void pyiType(sipSpec *pt, moduleDef *mod, argDef *ad, int out,
break;
default:
- type_name = anyObject(pep484);
+ type_name = "object";
}
if (type_name != NULL)
@@ -1093,8 +353,7 @@ void prScopedPythonName(FILE *fp, classDef *scope, const char *pyname)
* Generate a Python signature.
*/
static void pyiPythonSignature(sipSpec *pt, moduleDef *mod, signatureDef *sd,
- int need_self, ifaceFileList *defined, KwArgs kwargs, int pep484,
- FILE *fp)
+ int need_self, KwArgs kwargs, FILE *fp)
{
int void_return, need_comma, is_res, nr_out, a;
@@ -1122,7 +381,7 @@ static void pyiPythonSignature(sipSpec *pt, moduleDef *mod, signatureDef *sd,
continue;
need_comma = pyiArgument(pt, mod, ad, a, FALSE, need_comma, TRUE, TRUE,
- defined, kwargs, pep484, fp);
+ kwargs, fp);
}
fprintf(fp, ")");
@@ -1141,11 +400,11 @@ static void pyiPythonSignature(sipSpec *pt, moduleDef *mod, signatureDef *sd,
fprintf(fp, " -> ");
if ((is_res && nr_out > 0) || nr_out > 1)
- fprintf(fp, "%sTuple[", (pep484 ? "typing." : ""));
+ fprintf(fp, "Tuple[");
if (is_res)
need_comma = pyiArgument(pt, mod, &sd->result, -1, TRUE, FALSE,
- FALSE, FALSE, defined, kwargs, pep484, fp);
+ FALSE, FALSE, kwargs, fp);
else
need_comma = FALSE;
@@ -1156,38 +415,12 @@ static void pyiPythonSignature(sipSpec *pt, moduleDef *mod, signatureDef *sd,
if (isOutArg(ad))
/* We don't want the name in the result tuple. */
need_comma = pyiArgument(pt, mod, ad, -1, TRUE, need_comma,
- FALSE, FALSE, defined, kwargs, pep484, fp);
+ FALSE, FALSE, kwargs, fp);
}
if ((is_res && nr_out > 0) || nr_out > 1)
fprintf(fp, "]");
}
- else if (pep484)
- {
- fprintf(fp, " -> None");
- }
-}
-
-
-/*
- * Generate the required indentation.
- */
-static void prIndent(int indent, FILE *fp)
-{
- while (indent--)
- fprintf(fp, " ");
-}
-
-
-/*
- * Generate a newline if not already done.
- */
-static int separate(int first, int indent, FILE *fp)
-{
- if (first)
- fprintf(fp, (indent ? "\n" : "\n\n"));
-
- return FALSE;
}
@@ -1195,32 +428,9 @@ static int separate(int first, int indent, FILE *fp)
* Generate a class reference, including its owning module if necessary and
* handling forward references if necessary.
*/
-static void prClassRef(classDef *cd, moduleDef *mod, ifaceFileList *defined,
- int pep484, FILE *fp)
+static void prClassRef(classDef *cd, FILE *fp)
{
- if (pep484)
- {
- /*
- * We assume that an external class will be handled properly by some
- * handwritten type hint code.
- */
- int is_defined = (isExternal(cd) || isDefined(cd->iff, cd->ecd, mod, defined));
-
- if (!is_defined)
- fprintf(fp, "'");
-
- if (cd->iff->module != mod)
- fprintf(fp, "%s.", cd->iff->module->name);
-
- prScopedPythonName(fp, cd->ecd, cd->pyname->text);
-
- if (!is_defined)
- fprintf(fp, "'");
- }
- else
- {
- prScopedPythonName(fp, cd->ecd, cd->pyname->text);
- }
+ prScopedPythonName(fp, cd->ecd, cd->pyname->text);
}
@@ -1228,42 +438,9 @@ static void prClassRef(classDef *cd, moduleDef *mod, ifaceFileList *defined,
* Generate an enum reference, including its owning module if necessary and
* handling forward references if necessary.
*/
-static void prEnumRef(enumDef *ed, moduleDef *mod, ifaceFileList *defined,
- int pep484, FILE *fp)
+static void prEnumRef(enumDef *ed, FILE *fp)
{
- if (pep484)
- {
- int is_defined;
-
- if (ed->ecd != NULL)
- {
- is_defined = isDefined(ed->ecd->iff, ed->ecd->ecd, mod, defined);
- }
- else if (ed->emtd != NULL)
- {
- is_defined = isDefined(ed->emtd->iff, NULL, mod, defined);
- }
- else
- {
- /* Global enums are defined early on. */
- is_defined = TRUE;
- }
-
- if (!is_defined)
- fprintf(fp, "'");
-
- if (ed->module != mod)
- fprintf(fp, "%s.", ed->module->name);
-
- prScopedEnumName(fp, ed);
-
- if (!is_defined)
- fprintf(fp, "'");
- }
- else
- {
- prScopedEnumName(fp, ed);
- }
+ prScopedEnumName(fp, ed);
}
@@ -1279,75 +456,31 @@ static void prScopedEnumName(FILE *fp, enumDef *ed)
}
-/*
- * Check if a type has been defined.
- */
-static int isDefined(ifaceFileDef *iff, classDef *scope, moduleDef *mod,
- ifaceFileList *defined)
-{
- /* A type in another module would have been imported. */
- if (iff->module != mod)
- return TRUE;
-
- if (!inIfaceFileList(iff, defined))
- return FALSE;
-
- /* Check all enclosing scopes have been defined as well. */
- while (scope != NULL)
- {
- if (!inIfaceFileList(scope->iff, defined))
- return FALSE;
-
- scope = scope->ecd;
- }
-
- return TRUE;
-}
-
-
-/*
- * Check if an interface file appears in a list of them.
- */
-static int inIfaceFileList(ifaceFileDef *iff, ifaceFileList *defined)
-{
- while (defined != NULL)
- {
- if (defined->iff == iff)
- return TRUE;
-
- defined = defined->next;
- }
-
- return FALSE;
-}
-
-
/*
* Generate a type hint from a /TypeHint/ annotation.
*/
void pyiTypeHint(sipSpec *pt, typeHintDef *thd, moduleDef *mod, int out,
- ifaceFileList *defined, int pep484, int rest, FILE *fp)
+ FILE *fp)
{
parseTypeHint(pt, thd, out);
if (thd->root != NULL)
- pyiTypeHintNode(thd->root, mod, defined, pep484, rest, fp);
+ pyiTypeHintNode(thd->root, mod, fp);
else
- maybeAnyObject(thd->raw_hint, pep484, fp);
+ maybeAnyObject(thd->raw_hint, fp);
}
/*
* Generate a single node of a type hint.
*/
-static void pyiTypeHintNode(typeHintNodeDef *node, moduleDef *mod,
- ifaceFileList *defined, int pep484, int rest, FILE *fp)
+static void pyiTypeHintNode(typeHintNodeDef *node, moduleDef *mod, FILE *fp)
{
switch (node->type)
{
case typing_node:
if (node->u.name != NULL)
- fprintf(fp, "%s%s", (pep484 ? "typing." : ""), node->u.name);
+ fprintf(fp, "%s", node->u.name);
if (node->children != NULL)
{
@@ -1363,7 +496,7 @@ static void pyiTypeHintNode(typeHintNodeDef *node, moduleDef *mod,
need_comma = TRUE;
- pyiTypeHintNode(thnd, mod, defined, pep484, rest, fp);
+ pyiTypeHintNode(thnd, mod, fp);
}
fprintf(fp, "]");
@@ -1372,23 +505,15 @@ static void pyiTypeHintNode(typeHintNodeDef *node, moduleDef *mod,
break;
case class_node:
- if (rest)
- restPyClass(node->u.cd, fp);
- else
- prClassRef(node->u.cd, mod, defined, pep484, fp);
-
+ prClassRef(node->u.cd, fp);
break;
case enum_node:
- if (rest)
- restPyEnum(node->u.ed, fp);
- else
- prEnumRef(node->u.ed, mod, defined, pep484, fp);
-
+ prEnumRef(node->u.ed, fp);
break;
case other_node:
- maybeAnyObject(node->u.name, pep484, fp);
+ maybeAnyObject(node->u.name, fp);
break;
}
}
@@ -1860,9 +985,9 @@ static classDef *lookupClass(sipSpec *pt, const char *name, classDef *scope_cd)
/*
* Generate a hint taking into account that it may be any sort of object.
*/
-static void maybeAnyObject(const char *hint, int pep484, FILE *fp)
+static void maybeAnyObject(const char *hint, FILE *fp)
{
- fprintf(fp, "%s", (strcmp(hint, "Any") != 0 ? hint : anyObject(pep484)));
+ fprintf(fp, "%s", (strcmp(hint, "Any") != 0 ? hint : "object"));
}
@@ -1890,54 +1015,3 @@ static int isPyKeyword(const char *word)
return FALSE;
}
-
-
-/*
- * Add an interface file to an interface file list if it isn't already there.
- */
-static void appendToIfaceFileList(ifaceFileList **ifflp, ifaceFileDef *iff)
-{
- /* Make sure we don't try to add an interface file to its own list. */
- if (&iff->used != ifflp)
- {
- ifaceFileList *iffl;
-
- while ((iffl = *ifflp) != NULL)
- {
- /* Don't bother if it is already there. */
- if (iffl->iff == iff)
- return;
-
- ifflp = &iffl -> next;
- }
-
- iffl = sipMalloc(sizeof (ifaceFileList));
-
- iffl->iff = iff;
- iffl->next = NULL;
-
- *ifflp = iffl;
- }
-}
-
-
-/*
- * Generate a fully qualified class name as a reST reference.
- */
-void restPyClass(classDef *cd, FILE *fp)
-{
- fprintf(fp, ":sip:ref:`~%s.", cd->iff->module->fullname->text);
- prScopedPythonName(fp, cd->ecd, cd->pyname->text);
- fprintf(fp, "`");
-}
-
-
-/*
- * Generate a fully qualified enum name as a reST reference.
- */
-void restPyEnum(enumDef *ed, FILE *fp)
-{
- fprintf(fp, ":sip:ref:`~%s.", ed->module->fullname->text);
- prScopedPythonName(fp, ed->ecd, ed->pyname->text);
- fprintf(fp, "`");
-}
diff --git a/sip.egg-info/PKG-INFO b/sip.egg-info/PKG-INFO
index 48fe4c5..be2f1a9 100644
--- a/sip.egg-info/PKG-INFO
+++ b/sip.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: sip
-Version: 6.7.2
+Version: 6.7.3
Summary: A Python bindings generator for C/C++ libraries
Home-page: https://www.riverbankcomputing.com/software/sip/
Author: Riverbank Computing Limited
diff --git a/sip.egg-info/SOURCES.txt b/sip.egg-info/SOURCES.txt
index c9aa2a1..262f31f 100644
--- a/sip.egg-info/SOURCES.txt
+++ b/sip.egg-info/SOURCES.txt
@@ -8,7 +8,6 @@ README
pyproject.toml
setup.cfg
setup.py
-code_generator/export.c
code_generator/gencode.c
code_generator/heap.c
code_generator/py2c.c
@@ -137,23 +136,25 @@ sipbuild/generator/python_slots.py
sipbuild/generator/scoped_name.py
sipbuild/generator/specification.py
sipbuild/generator/templates.py
-sipbuild/generator/type_hints.py
sipbuild/generator/utils.py
-sipbuild/generator/formatters/__init__.py
-sipbuild/generator/formatters/argument.py
-sipbuild/generator/formatters/base_formatter.py
-sipbuild/generator/formatters/enum.py
-sipbuild/generator/formatters/klass.py
-sipbuild/generator/formatters/overload.py
-sipbuild/generator/formatters/scoped.py
-sipbuild/generator/formatters/signature.py
-sipbuild/generator/formatters/template.py
-sipbuild/generator/formatters/utils.py
-sipbuild/generator/formatters/value_list.py
-sipbuild/generator/formatters/variable.py
sipbuild/generator/outputs/__init__.py
sipbuild/generator/outputs/api.py
sipbuild/generator/outputs/extracts.py
+sipbuild/generator/outputs/pyi.py
+sipbuild/generator/outputs/type_hints.py
+sipbuild/generator/outputs/xml.py
+sipbuild/generator/outputs/formatters/__init__.py
+sipbuild/generator/outputs/formatters/argument.py
+sipbuild/generator/outputs/formatters/base_formatter.py
+sipbuild/generator/outputs/formatters/enum.py
+sipbuild/generator/outputs/formatters/klass.py
+sipbuild/generator/outputs/formatters/overload.py
+sipbuild/generator/outputs/formatters/scoped.py
+sipbuild/generator/outputs/formatters/signature.py
+sipbuild/generator/outputs/formatters/template.py
+sipbuild/generator/outputs/formatters/utils.py
+sipbuild/generator/outputs/formatters/value_list.py
+sipbuild/generator/outputs/formatters/variable.py
sipbuild/generator/parser/__init__.py
sipbuild/generator/parser/annotations.py
sipbuild/generator/parser/parser.py
diff --git a/sipbuild/bindings.py b/sipbuild/bindings.py
index 1c254a4..7f0a40e 100644
--- a/sipbuild/bindings.py
+++ b/sipbuild/bindings.py
@@ -25,11 +25,11 @@ import os
import sys
from .buildable import BuildableBindings
-from .code_generator import generateCode, generateTypeHints, py2c
+from .code_generator import generateCode, py2c
from .configurable import Configurable, Option
from .exceptions import UserException
from .generator import parse, resolve
-from .generator.outputs import output_api, output_extract
+from .generator.outputs import output_api, output_extract, output_pyi
from .installable import Installable
from .module import copy_nonshared_sources
from .version import SIP_VERSION
@@ -165,7 +165,8 @@ class Bindings(Configurable):
# Parse the input file.
spec, sip_files = parse(self.sip_file, SIP_VERSION, encoding,
project.abi_version, self.tags, self.disabled_features,
- self.protected_is_public, self._sip_include_dirs)
+ self.protected_is_public, self._sip_include_dirs,
+ project.sip_module or 'sip')
# Resolve the types.
resolve(spec)
@@ -195,7 +196,7 @@ class Bindings(Configurable):
project.progress(
"Generating the {0} .api file".format(buildable.target))
- output_api(spec, module,
+ output_api(spec,
os.path.join(project.build_dir, buildable.target + '.api'))
# Generate any extracts.
@@ -210,7 +211,7 @@ class Bindings(Configurable):
pyi_path = os.path.join(buildable.build_dir,
buildable.target + '.pyi')
- generateTypeHints(pt, pyi_path)
+ output_pyi(spec, pyi_path)
installable = Installable('pyi',
target_subdir=buildable.get_install_subdir())
diff --git a/sipbuild/generator/instantiations.py b/sipbuild/generator/instantiations.py
index 342ad0e..7885d0d 100644
--- a/sipbuild/generator/instantiations.py
+++ b/sipbuild/generator/instantiations.py
@@ -28,7 +28,6 @@ from .specification import (Argument, ArgumentType, FunctionCall,
IfaceFileType, KwArgs, Signature, TypeHints, Value, ValueType)
from .templates import (template_code, template_code_blocks,
template_expansions, template_string)
-from .type_hints import get_type_hint
from .utils import append_iface_file, cached_name, normalised_scoped_name
@@ -391,16 +390,14 @@ def instantiate_type_hints(spec, proto_type_hints, expansions):
""" Return an instantiated TypeHints object. """
if proto_type_hints.hint_in is not None:
- hint_in = get_type_hint(spec,
- template_string(proto_type_hints.hint_in.text, expansions,
- scope_replacement='.'))
+ hint_in = template_string(proto_type_hints.hint_in, expansions,
+ scope_replacement='.')
else:
hint_in = None
if proto_type_hints.hint_out is not None:
- hint_out = get_type_hint(spec,
- template_string(proto_type_hints.hint_out.text, expansions,
- scope_replacement='.'))
+ hint_out = template_string(proto_type_hints.hint_out, expansions,
+ scope_replacement='.')
else:
hint_out = None
diff --git a/sipbuild/generator/outputs/__init__.py b/sipbuild/generator/outputs/__init__.py
index d34eb36..c2bada1 100644
--- a/sipbuild/generator/outputs/__init__.py
+++ b/sipbuild/generator/outputs/__init__.py
@@ -24,3 +24,4 @@
# Publish the API. This is private to the rest of sip.
from .api import output_api
from .extracts import output_extract
+from .pyi import output_pyi
diff --git a/sipbuild/generator/outputs/api.py b/sipbuild/generator/outputs/api.py
index 05e2ce1..4a5fde0 100644
--- a/sipbuild/generator/outputs/api.py
+++ b/sipbuild/generator/outputs/api.py
@@ -23,10 +23,11 @@
from enum import IntEnum
-from ..formatters import (ClassFormatter, EnumFormatter, OverloadFormatter,
- SignatureFormatter, VariableFormatter)
from ..specification import AccessSpecifier
+from .formatters import (ClassFormatter, EnumFormatter, OverloadFormatter,
+ SignatureFormatter, VariableFormatter)
+
class IconNumber(IntEnum):
""" The numbers of the different icons. The values are those used by the
@@ -39,32 +40,34 @@ class IconNumber(IntEnum):
ENUM = 10
-def output_api(spec, module, api_filename):
+def output_api(spec, api_filename):
""" Output a QScintilla API file. """
with open(api_filename, 'w') as af:
- _api_enums(af, spec, module)
- _api_variables(af, spec, module)
+ module = spec.modules[0]
+
+ _enums(af, spec, module)
+ _variables(af, spec, module)
for overload in module.overloads:
if overload.common.module is module and overload.common.py_slot is None:
- _api_overload(af, spec, module, overload)
+ _overload(af, spec, module, overload)
for klass in spec.classes:
if klass.iface_file.module is module and not klass.external:
- _api_enums(af, spec, module, scope=klass)
- _api_variables(af, spec, module, scope=klass)
+ _enums(af, spec, module, scope=klass)
+ _variables(af, spec, module, scope=klass)
for ctor in klass.ctors:
if ctor.access_specifier is not AccessSpecifier.PRIVATE:
- _api_ctor(af, spec, module, ctor, klass)
+ _ctor(af, spec, module, ctor, klass)
for overload in klass.overloads:
if overload.access_specifier is not AccessSpecifier.PRIVATE and overload.common.py_slot is None:
- _api_overload(af, spec, module, overload, scope=klass)
+ _overload(af, spec, module, overload, scope=klass)
-def _api_ctor(af, spec, module, ctor, scope):
+def _ctor(af, spec, module, ctor, scope):
""" Generate an API ctor. """
py_class = module.py_name + '.' + ClassFormatter(spec, scope).fq_py_name
@@ -82,7 +85,7 @@ def _api_ctor(af, spec, module, ctor, scope):
af.write(f'{py_class}.__init__?{IconNumber.CLASS}({py_arguments})\n')
-def _api_enums(af, spec, module, scope=None):
+def _enums(af, spec, module, scope=None):
""" Generate the APIs for all the enums in a scope. """
for enum in spec.enums:
@@ -96,7 +99,7 @@ def _api_enums(af, spec, module, scope=None):
af.write(f'{member_s}?{IconNumber.ENUM}\n')
-def _api_variables(af, spec, module, scope=None):
+def _variables(af, spec, module, scope=None):
""" Generate the APIs for all the variables in a scope. """
for variable in spec.variables:
@@ -106,7 +109,7 @@ def _api_variables(af, spec, module, scope=None):
af.write(f'{module.py_name}.{formatter.fq_py_name}?{IconNumber.VARIABLE}\n')
-def _api_overload(af, spec, module, overload, scope=None):
+def _overload(af, spec, module, overload, scope=None):
""" Generate a single API overload. """
sig_formatter = SignatureFormatter(spec, overload.py_signature)
diff --git a/sipbuild/generator/formatters/__init__.py b/sipbuild/generator/outputs/formatters/__init__.py
similarity index 91%
rename from sipbuild/generator/formatters/__init__.py
rename to sipbuild/generator/outputs/formatters/__init__.py
index 562c8fd..0d4afcb 100644
--- a/sipbuild/generator/formatters/__init__.py
+++ b/sipbuild/generator/outputs/formatters/__init__.py
@@ -22,8 +22,11 @@
# Publish the API. This is private to the rest of sip.
+from .argument import ArgumentFormatter
from .enum import EnumFormatter
from .klass import ClassFormatter
from .overload import OverloadFormatter
from .signature import SignatureFormatter
+from .utils import format_copying, format_scoped_py_name
+from .value_list import ValueListFormatter
from .variable import VariableFormatter
diff --git a/sipbuild/generator/formatters/argument.py b/sipbuild/generator/outputs/formatters/argument.py
similarity index 70%
rename from sipbuild/generator/formatters/argument.py
rename to sipbuild/generator/outputs/formatters/argument.py
index 6d518f7..f060e85 100644
--- a/sipbuild/generator/formatters/argument.py
+++ b/sipbuild/generator/outputs/formatters/argument.py
@@ -21,8 +21,10 @@
# POSSIBILITY OF SUCH DAMAGE.
-from ..scoped_name import STRIP_NONE
-from ..specification import ArgumentType, ArrayArgument, ClassKey, ValueType
+from ...scoped_name import STRIP_NONE
+from ...specification import ArgumentType, ArrayArgument, ClassKey, ValueType
+
+from ..type_hints import TypeHintManager
from .base_formatter import BaseFormatter
from .utils import format_scoped_py_name
@@ -32,8 +34,8 @@ class ArgumentFormatter(BaseFormatter):
""" This creates various string representations of an argument. """
def cpp_type(self, *, name=None, scope=None, strip=STRIP_NONE,
- use_typename=True, as_xml=False):
- """ Return the C++ representation of the argument type. """
+ make_public=False, use_typename=True, as_xml=False):
+ """ Return the argument as a C++ type. """
arg = self.object
@@ -61,7 +63,7 @@ class ArgumentFormatter(BaseFormatter):
if arg.type is ArgumentType.FUNCTION:
s += ArgumentFormatter(self.spec,
arg.definition.result).cpp_type(scope=scope,
- strip=strip)
+ strip=strip, as_xml=as_xml)
s += ' (' + '*' * nr_derefs + name + ')('
@@ -158,7 +160,8 @@ class ArgumentFormatter(BaseFormatter):
elif arg.type is ArgumentType.MAPPED:
s += ArgumentFormatter(self.spec,
- arg.definition.type).cpp_type(scope=scope, strip=strip)
+ arg.definition.type).cpp_type(scope=scope, strip=strip,
+ as_xml=as_xml)
elif arg.type is ArgumentType.CLASS:
from .klass import ClassFormatter
@@ -167,18 +170,19 @@ class ArgumentFormatter(BaseFormatter):
s += 'union ' if arg.definition.class_key is ClassKey.UNION else 'struct '
s += ClassFormatter(self.spec, arg.definition).scoped_name(
- scope=scope, strip=strip, as_xml=as_xml)
+ scope=scope, strip=strip, make_public=make_public,
+ as_xml=as_xml)
elif arg.type is ArgumentType.TEMPLATE:
from .template import TemplateFormatter
- s += TemplateFormatter(self.spec, arg.definition).cpp_type(
- scope=scope, strip=strip, as_xml=as_xml)
+ s += TemplateFormatter(self.spec, arg.definition, scope).cpp_type(
+ strip=strip, as_xml=as_xml)
elif arg.type is ArgumentType.ENUM:
enum = arg.definition
- if enum.fq_cpp_name is None or enum.is_protected:
+ if enum.fq_cpp_name is None or (enum.is_protected and not make_public):
s += 'int'
else:
s += enum.fq_cpp_name.cpp_stripped(strip)
@@ -199,7 +203,7 @@ class ArgumentFormatter(BaseFormatter):
space_before_name = True
if is_reference:
- s += '&' if as_xml else '&'
+ s += '&'
if name:
if space_before_name:
@@ -209,12 +213,37 @@ class ArgumentFormatter(BaseFormatter):
return s
- def py_type(self, default_value=False):
- """ Return the Python representation of the argument type. """
+ def py_default_value(self, embedded=False):
+ """ Return the Python representation of the argument's default value.
+ """
+
+ from .value_list import ValueListFormatter
+
+ arg = self.object
+
+ # Use any explicitly provided documentation.
+ if arg.type_hints is not None and arg.type_hints.default_value is not None:
+ return arg.type_hints.default_value
+
+ # Translate some special cases.
+ if len(arg.default_value) == 1 and arg.default_value[0].value_type is ValueType.NUMERIC:
+ value = arg.default_value[0].value
+
+ if len(arg.derefs) > 0 and value == 0:
+ return 'None'
+
+ if arg.type in (ArgumentType.BOOL, ArgumentType.CBOOL):
+ return 'True' if value else 'False'
+
+ return ValueListFormatter(self.spec, arg.default_value).py_expression(
+ embedded=embedded)
+
+ def as_py_type(self, pep484=False, default_value=False):
+ """ Return the argument as a Python type. """
arg = self.object
- scope, name = self._py_arg()
+ scope, name = self._py_arg(pep484)
s = format_scoped_py_name(scope, name)
@@ -222,15 +251,98 @@ class ArgumentFormatter(BaseFormatter):
if arg.name is not None:
s += ' ' + arg.name.name
- s += '=' + self._py_default_value()
+ s += '=' + self.py_default_value()
+
+ return s
+
+ def as_rest_ref(self, out):
+ """ Return the argument as a reST reference. """
+
+ arg = self.object
+
+ s = ''
+
+ hint = self._get_hint(out)
+
+ if hint is None:
+ if arg.type is ArgumentType.CLASS:
+ from .klass import ClassFormatter
+
+ s += ClassFormatter(self.spec, arg.definition).as_rest_ref()
+ elif arg.type is ArgumentType.ENUM:
+ if arg.definition.py_name is not None:
+ from .enum import EnumFormatter
+
+ s += EnumFormatter(self.spec, arg.definition).as_rest_ref()
+ else:
+ s += 'int'
+ elif arg.type is ArgumentType.MAPPED:
+ # There would normally be a type hint.
+ s += "unknown-type"
+ else:
+ s += self.as_py_type()
+ else:
+ s += TypeHintManager(self.spec).as_rest_ref(hint, out)
return s
- def _py_arg(self):
+ def as_type_hint(self, module, out, defined):
+ """ Return the argument as a type hint. """
+
+ arg = self.object
+
+ s = ''
+
+ hint = self._get_hint(out)
+
+ if hint is None:
+ if arg.type is ArgumentType.CLASS:
+ from .klass import ClassFormatter
+
+ s += ClassFormatter(self.spec, arg.definition).as_type_hint(
+ module, defined)
+ elif arg.type is ArgumentType.ENUM:
+ if arg.definition.py_name is not None:
+ from .enum import EnumFormatter
+
+ s += EnumFormatter(self.spec, arg.definition).as_type_hint(
+ module, defined)
+ else:
+ s += 'int'
+ elif arg.type is ArgumentType.MAPPED:
+ # There would normally be a type hint.
+ s += 'typing.Any'
+ else:
+ s += self.as_py_type(pep484=True)
+ else:
+ s += TypeHintManager(self.spec).as_type_hint(hint, module, out,
+ defined)
+
+ return s
+
+ def _get_hint(self, out):
+ """ Return the raw type hint. """
+
+ arg = self.object
+
+ # Use any explicit type hint unless the argument is constrained.
+ if arg.type_hints is None:
+ hint = None
+ elif out:
+ hint = arg.type_hints.hint_out
+ elif arg.is_constrained:
+ hint = None
+ else:
+ hint = arg.type_hints.hint_in
+
+ return hint
+
+ def _py_arg(self, pep484):
""" Return an argument as a 2-tuple of scope and name. """
type = self.object.type
definition = self.object.definition
+ sip_module = self.spec.sip_module
scope = None
name = "unknown-type"
@@ -243,6 +355,13 @@ class ArgumentFormatter(BaseFormatter):
if definition.py_name is not None:
name = definition.py_name.name
+ elif type is ArgumentType.ENUM:
+ if definition.py_name is None:
+ name = 'int'
+ else:
+ scope = definition.scope
+ name = definition.py_name.name
+
elif type is ArgumentType.DEFINED:
name = definition.as_py
@@ -250,19 +369,12 @@ class ArgumentFormatter(BaseFormatter):
name = definition.base_name
elif type in (ArgumentType.STRUCT, ArgumentType.UNION, ArgumentType.VOID):
- name = 'sip.voidptr'
+ name = sip_module + '.voidptr'
- elif type is ArgumentType.ENUM:
- if definition.py_name is None:
- name = 'int'
- else:
- scope = definition.scope
- name = definition.py_name.name
-
- elif type is ArgumentType.USTRING:
+ elif type in (ArgumentType.STRING, ArgumentType.SSTRING, ArgumentType.USTRING):
name = 'bytes'
- elif type in (ArgumentType.STRING, ArgumentType.SSTRING, ArgumentType.WSTRING, ArgumentType.ASCII_STRING, ArgumentType.LATIN1_STRING, ArgumentType.UTF8_STRING):
+ elif type in (ArgumentType.WSTRING, ArgumentType.ASCII_STRING, ArgumentType.LATIN1_STRING, ArgumentType.UTF8_STRING):
name = 'bytes' if self.object.array is ArrayArgument.ARRAY else 'str'
elif type in (ArgumentType.BYTE, ArgumentType.SBYTE, ArgumentType.UBYTE, ArgumentType.USHORT, ArgumentType.UINT, ArgumentType.LONG, ArgumentType.LONGLONG, ArgumentType.ULONG, ArgumentType.ULONGLONG, ArgumentType.SHORT, ArgumentType.INT, ArgumentType.CINT, ArgumentType.SSIZE, ArgumentType.SIZE, ArgumentType.HASH):
@@ -275,19 +387,19 @@ class ArgumentFormatter(BaseFormatter):
name = 'bool'
elif type is ArgumentType.PYOBJECT:
- name = 'object'
+ name = 'typing.Any' if pep484 else 'Any'
elif type is ArgumentType.PYTUPLE:
- name = 'tuple'
+ name = 'typing.Tuple' if pep484 else 'Tuple'
elif type is ArgumentType.PYLIST:
- name = 'list'
+ name = 'typing.List' if pep484 else 'List'
elif type is ArgumentType.PYDICT:
- name = 'dict'
+ name = 'typing.Dict' if pep484 else 'Dict'
elif type is ArgumentType.PYCALLABLE:
- name = 'callable'
+ name = 'typing.Callable[..., None]' if pep484 else 'Callable[..., None]'
elif type is ArgumentType.PYSLICE:
name = 'slice'
@@ -296,36 +408,16 @@ class ArgumentFormatter(BaseFormatter):
name = 'type'
elif type is ArgumentType.PYBUFFER:
- name = 'buffer'
+ if pep484:
+ name = sip_module + '.Buffer'
+ else:
+ # This replicates sip.pyi.
+ name = f'Union[bytes, bytearray, memoryview, {sip_module}.array, {sip_module}.voidptr]'
elif type is ArgumentType.PYENUM:
- name = 'enum'
+ name = 'enum.Enum'
elif type is ArgumentType.ELLIPSIS:
- name = '...'
+ name = '*'
return scope, name
-
- def _py_default_value(self):
- """ Return the Python representation of the argument's default value.
- """
-
- from .value_list import ValueListFormatter
-
- arg = self.object
-
- # Use any explicitly provided documentation.
- if arg.type_hints is not None and arg.type_hints.default_value is not None:
- return arg.type_hints.default_value
-
- # Translate some special cases.
- if len(arg.default_value) == 1 and arg.default_value[0].value_type is ValueType.NUMERIC:
- value = arg.default_value[0].value
-
- if len(arg.derefs) > 0 and value == 0:
- return 'None'
-
- if arg.type in (ArgumentType.BOOL, ArgumentType.CBOOL):
- return 'True' if value else 'False'
-
- return ValueListFormatter(self.spec, arg.default_value).py_expression
diff --git a/sipbuild/generator/formatters/base_formatter.py b/sipbuild/generator/outputs/formatters/base_formatter.py
similarity index 100%
rename from sipbuild/generator/formatters/base_formatter.py
rename to sipbuild/generator/outputs/formatters/base_formatter.py
diff --git a/sipbuild/generator/formatters/enum.py b/sipbuild/generator/outputs/formatters/enum.py
similarity index 50%
rename from sipbuild/generator/formatters/enum.py
rename to sipbuild/generator/outputs/formatters/enum.py
index eb6e5d8..889f3a6 100644
--- a/sipbuild/generator/formatters/enum.py
+++ b/sipbuild/generator/outputs/formatters/enum.py
@@ -21,8 +21,10 @@
# POSSIBILITY OF SUCH DAMAGE.
+from ...specification import IfaceFileType
+
from .scoped import EmbeddedScopeFormatter
-from .utils import format_scoped_py_name
+from .utils import format_scoped_py_name, iface_is_defined
class EnumFormatter(EmbeddedScopeFormatter):
@@ -44,3 +46,53 @@ class EnumFormatter(EmbeddedScopeFormatter):
for member in enum.members:
yield enum_name + member.py_name.name
+
+ def member_as_rest_ref(self, member):
+ """ Return the fully qualified Python name of a member as a reST
+ reference.
+ """
+
+ enum = self.object
+ module_name = enum.module.fq_py_name.name
+
+ if enum.py_name is None:
+ member_name = format_scoped_py_name(self.scope,
+ member.py_name.name)
+
+ return f':sip:ref:`~{module_name}.{member_name}`'
+
+ enum_name = format_scoped_py_name(self.scope, enum.py_name.name)
+ member_name = member.py_name.name
+
+ return f':sip:ref:`~{module_name}.{enum_name}.{member_name}`'
+
+ def as_rest_ref(self):
+ """ Return the fully qualified Python name as a reST reference. """
+
+ enum = self.object
+ module_name = enum.module.fq_py_name.name
+ enum_name = format_scoped_py_name(self.scope, enum.py_name.name)
+
+ return f':sip:ref:`~{module_name}.{enum_name}`'
+
+ def as_type_hint(self, module, defined):
+ """ Return the type hint. """
+
+ enum = self.object
+
+ if self.scope is None:
+ # Global enums are defined early on.
+ is_defined = True
+ else:
+ scope_iface = self.scope.iface_file
+ outer_scope = self.scope.scope if scope_iface.type is IfaceFileType.CLASS else None
+
+ is_defined = iface_is_defined(scope_iface, outer_scope, module,
+ defined)
+
+ quote = '' if is_defined else "'"
+
+ # Include the module name if it is not the current one.
+ module_name = enum.module.py_name + '.' if enum.module is not module else ''
+
+ return f'{quote}{module_name}{self.fq_py_name}{quote}'
diff --git a/sipbuild/generator/formatters/klass.py b/sipbuild/generator/outputs/formatters/klass.py
similarity index 63%
rename from sipbuild/generator/formatters/klass.py
rename to sipbuild/generator/outputs/formatters/klass.py
index d680431..6c41637 100644
--- a/sipbuild/generator/formatters/klass.py
+++ b/sipbuild/generator/outputs/formatters/klass.py
@@ -21,16 +21,27 @@
# POSSIBILITY OF SUCH DAMAGE.
-from ..scoped_name import STRIP_NONE
+from ...scoped_name import STRIP_NONE
from .scoped import EmbeddedScopeFormatter
from .template import TemplateFormatter
+from .utils import format_scoped_py_name, iface_is_defined
class ClassFormatter(EmbeddedScopeFormatter):
""" This creates various string representations of a class. """
- def scoped_name(self, *, scope=None, strip=STRIP_NONE, as_xml=False):
+ def as_rest_ref(self):
+ """ Return the fully qualified Python name as a reST reference. """
+
+ klass = self.object
+ module_name = klass.iface_file.module.fq_py_name.name
+ klass_name = format_scoped_py_name(self.scope, klass.py_name.name)
+
+ return f':sip:ref:`~{module_name}.{klass_name}`'
+
+ def scoped_name(self, *, scope=None, strip=STRIP_NONE, make_public=False,
+ as_xml=False):
""" Return an appropriately scoped class name. """
klass = self.object
@@ -40,7 +51,7 @@ class ClassFormatter(EmbeddedScopeFormatter):
scope=scope, strip=strip, as_xml=as_xml)
# Protected classes have to be explicitly scoped.
- if klass.is_protected:
+ if klass.is_protected and not make_public:
# This should never happen.
if scope is None:
scope = klass.iface_file
@@ -48,3 +59,17 @@ class ClassFormatter(EmbeddedScopeFormatter):
return 'sip{scope.fq_cpp_name.as_word}::sip{klass.iface_file.fq_cpp_name.base_name}'
return klass.iface_file.fq_cpp_name.cpp_stripped(strip)
+
+ def as_type_hint(self, module, defined):
+ """ Return the type hint. """
+
+ klass = self.object
+
+ # We assume that an external class will be handled properly by some
+ # handwritten type hint code.
+ quote = '' if klass.external or iface_is_defined(klass.iface_file, klass.scope, module, defined) else "'"
+
+ # Include the module name if it is not the current one.
+ module_name = klass.iface_file.module.py_name + '.' if klass.iface_file.module is not module else ''
+
+ return f'{quote}{module_name}{self.fq_py_name}{quote}'
diff --git a/sipbuild/generator/formatters/overload.py b/sipbuild/generator/outputs/formatters/overload.py
similarity index 98%
rename from sipbuild/generator/formatters/overload.py
rename to sipbuild/generator/outputs/formatters/overload.py
index 3f0b0da..dc78106 100644
--- a/sipbuild/generator/formatters/overload.py
+++ b/sipbuild/generator/outputs/formatters/overload.py
@@ -21,7 +21,7 @@
# POSSIBILITY OF SUCH DAMAGE.
-from ..specification import PySlot
+from ...specification import PySlot
from .scoped import ScopedFormatter
from .utils import format_scoped_py_name
diff --git a/sipbuild/generator/formatters/scoped.py b/sipbuild/generator/outputs/formatters/scoped.py
similarity index 100%
rename from sipbuild/generator/formatters/scoped.py
rename to sipbuild/generator/outputs/formatters/scoped.py
diff --git a/sipbuild/generator/formatters/signature.py b/sipbuild/generator/outputs/formatters/signature.py
similarity index 80%
rename from sipbuild/generator/formatters/signature.py
rename to sipbuild/generator/outputs/formatters/signature.py
index 17be380..e9070fd 100644
--- a/sipbuild/generator/formatters/signature.py
+++ b/sipbuild/generator/outputs/formatters/signature.py
@@ -21,8 +21,8 @@
# POSSIBILITY OF SUCH DAMAGE.
-from ..scoped_name import STRIP_NONE
-from ..specification import ArgumentType, ArrayArgument
+from ...scoped_name import STRIP_NONE
+from ...specification import ArgumentType, ArrayArgument
from .argument import ArgumentFormatter
from .base_formatter import BaseFormatter
@@ -31,14 +31,17 @@ from .base_formatter import BaseFormatter
class SignatureFormatter(BaseFormatter):
""" This creates various string representations of a signature. """
- def cpp_arguments(self, *, scope=None, strip=STRIP_NONE,
+ def cpp_arguments(self, *, scope=None, strip=STRIP_NONE, make_public=False,
as_xml=False):
""" Return the C++ representation of the signature arguments. """
args = [ArgumentFormatter(self.spec, arg).cpp_type(scope=scope,
- strip=strip, as_xml=as_xml) for arg in self.object.args]
+ strip=strip, make_public=make_public, as_xml=as_xml)
+ for arg in self.object.args]
- return ', '.join(args)
+ # Note the lack of separating space (although this may be for XML only
+ # to be consistent with previous implementations).
+ return ','.join(args)
@property
def py_arguments(self):
@@ -50,7 +53,7 @@ class SignatureFormatter(BaseFormatter):
if arg.array is not ArrayArgument.ARRAY_SIZE and arg.is_in:
args.append(
ArgumentFormatter(self.spec,
- arg).py_type(default_value=True))
+ arg).as_py_type(default_value=True))
return ', '.join(args)
@@ -65,10 +68,10 @@ class SignatureFormatter(BaseFormatter):
if sig.result is not None:
if sig.result.type is not ArgumentType.VOID or len(sig.result.derefs) != 0:
results.append(ArgumentFormatter(self.spec,
- sig.result).py_type())
+ sig.result).as_py_type())
for arg in sig.args:
if arg.is_out:
- results.append(ArgumentFormatter(self.spec, arg).py_type())
+ results.append(ArgumentFormatter(self.spec, arg).as_py_type())
return ', '.join(results)
diff --git a/sipbuild/generator/formatters/template.py b/sipbuild/generator/outputs/formatters/template.py
similarity index 85%
rename from sipbuild/generator/formatters/template.py
rename to sipbuild/generator/outputs/formatters/template.py
index ae1df10..c7491a0 100644
--- a/sipbuild/generator/formatters/template.py
+++ b/sipbuild/generator/outputs/formatters/template.py
@@ -21,7 +21,7 @@
# POSSIBILITY OF SUCH DAMAGE.
-from ..scoped_name import STRIP_GLOBAL, STRIP_NONE
+from ...scoped_name import STRIP_GLOBAL, STRIP_NONE
from .scoped import ScopedFormatter
from .signature import SignatureFormatter
@@ -30,7 +30,7 @@ from .signature import SignatureFormatter
class TemplateFormatter(ScopedFormatter):
""" This creates various string representations of a template. """
- def cpp_type(self, *, scope=None, strip=STRIP_NONE, as_xml=False):
+ def cpp_type(self, *, strip=STRIP_NONE, as_xml=False):
""" Return the C++ representation of the template type. """
template = self.object
@@ -42,17 +42,14 @@ class TemplateFormatter(ScopedFormatter):
s += template.cpp_name.cpp_stripped(strip)
- s += '<' if as_xml else '<'
+ s += '<'
s += SignatureFormatter(self.spec, template.types).cpp_arguments(
strip=strip, as_xml=as_xml)
- if as_xml:
- s += '>'
- else:
- if s.endswith('>'):
- s += ' '
+ if s.endswith('>') and not as_xml:
+ s += ' '
- s += '>'
+ s += '>'
return s
diff --git a/sipbuild/generator/formatters/utils.py b/sipbuild/generator/outputs/formatters/utils.py
similarity index 66%
rename from sipbuild/generator/formatters/utils.py
rename to sipbuild/generator/outputs/formatters/utils.py
index 242742a..b08ec09 100644
--- a/sipbuild/generator/formatters/utils.py
+++ b/sipbuild/generator/outputs/formatters/utils.py
@@ -21,6 +21,17 @@
# POSSIBILITY OF SUCH DAMAGE.
+def format_copying(copying, comment):
+ """ Return a formatted %Copying text. """
+
+ s = comment + ' ' + ''.join([b.text for b in copying]).rstrip().replace('\n', '\n' + comment + ' ')
+
+ if s:
+ s = comment + '\n' + s
+
+ return s
+
+
def format_scoped_py_name(scope, py_name):
""" Return a formatted scoped Python name. """
@@ -35,3 +46,25 @@ def format_scoped_py_name(scope, py_name):
py_name_s = py_name
return scope_s + py_name_s
+
+
+def iface_is_defined(iface_file, scope, module, defined):
+ """ Return True if a type corresponding to an interface file has been
+ defined in the context of a module.
+ """
+
+ # A type in another module would have been imported.
+ if iface_file.module is not module:
+ return True
+
+ if iface_file not in defined:
+ return False
+
+ # Check all enclosing scopes have been defined as well.
+ while scope is not None:
+ if scope.iface_file not in defined:
+ return False
+
+ scope = scope.scope
+
+ return True
diff --git a/sipbuild/generator/formatters/value_list.py b/sipbuild/generator/outputs/formatters/value_list.py
similarity index 65%
rename from sipbuild/generator/formatters/value_list.py
rename to sipbuild/generator/outputs/formatters/value_list.py
index 031a9d6..0de7314 100644
--- a/sipbuild/generator/formatters/value_list.py
+++ b/sipbuild/generator/outputs/formatters/value_list.py
@@ -21,10 +21,12 @@
# POSSIBILITY OF SUCH DAMAGE.
-from ..specification import ValueType
+from ...specification import ValueType
from .argument import ArgumentFormatter
from .base_formatter import BaseFormatter
+from .enum import EnumFormatter
+from .variable import VariableFormatter
class ValueListFormatter(BaseFormatter):
@@ -36,19 +38,57 @@ class ValueListFormatter(BaseFormatter):
return self._expression()
- @property
- def embedded_py_expression(self):
- """ The Python representation of the value list as an expression
- embedded in a string.
+ def py_expression(self, embedded=False):
+ """ The Python representation of the value list as an expression. """
+
+ return self._expression(as_python=True, embedded=embedded)
+
+ def as_rest_ref(self):
+ """ Return the Python representation of the value list as a reST
+ reference.
"""
- return self._expression(as_python=True, embedded=True)
+ value_list = self.object
- @property
- def py_expression(self):
- """ The Python representation of the value list as an expression. """
+ # The value must be a scoped name and we don't handle expressions.
+ if len(value_list) != 1 or value_list[0].value_type is not ValueType.SCOPED:
+ return None
+
+ target = value_list[0].value
+
+ # See if it is an attribute.
+ for variable in self.spec.variables:
+ if variable.fq_cpp_name == target:
+ return VariableFormatter(self.spec, variable).as_rest_ref()
+
+ # See if it is an enum member.
+ target_scope = target.scope
+ if target_scope is not None:
+ target_scope.make_absolute()
+
+ target_base_name = target.base_name
+
+ for enum in self.spec.enums:
+ # Look for the member name first before working out if it is the
+ # correct enum.
+ for member in enum.members:
+ if member.cpp_name == target_base_name:
+ formatter = EnumFormatter(self.spec, enum)
+
+ if enum.is_scoped:
+ # It's a scoped enum so the fully qualified name of the
+ # enum must match the scope of the name.
+ if target_scope is not None and enum.fq_cpp_name == target_scope:
+ return formatter.member_as_rest_ref(member)
+ else:
+ # It's a traditional enum so the scope of the enum must
+ # match the scope of the name.
+ if (enum.scope is None and target_scope is None) or (enum.scope is not None and target_scope is not None and enum.scope.iface_file.fq_cpp_name == target_scope):
+ return formatter.member_as_rest_ref(member)
+
+ break
- return self._expression(as_python=True)
+ return None
def _expression(self, as_python=False, embedded=False):
""" The representation of the value list as an expression. """
@@ -110,7 +150,7 @@ class ValueListFormatter(BaseFormatter):
value.value.result)
if as_python:
- s += arg_formatter.py_type()
+ s += arg_formatter.as_py_type()
else:
s += arg_formatter.cpp_type()
diff --git a/sipbuild/generator/formatters/variable.py b/sipbuild/generator/outputs/formatters/variable.py
similarity index 79%
rename from sipbuild/generator/formatters/variable.py
rename to sipbuild/generator/outputs/formatters/variable.py
index 41d340c..ea7142f 100644
--- a/sipbuild/generator/formatters/variable.py
+++ b/sipbuild/generator/outputs/formatters/variable.py
@@ -26,3 +26,13 @@ from .scoped import EmbeddedScopeFormatter
class VariableFormatter(EmbeddedScopeFormatter):
""" This creates various string representations of a variable. """
+
+ def as_rest_ref(self):
+ """ Return the fully qualified Python name as a reST reference. """
+
+ variable = self.object
+ module_name = variable.module.fq_py_name.name
+ variable_name = format_scoped_py_name(self.scope,
+ variable.py_name.name)
+
+ return f':sip:ref:`~{module_name}.{variable_name}`'
diff --git a/sipbuild/generator/outputs/pyi.py b/sipbuild/generator/outputs/pyi.py
new file mode 100644
index 0000000..4c3705c
--- /dev/null
+++ b/sipbuild/generator/outputs/pyi.py
@@ -0,0 +1,685 @@
+# Copyright (c) 2022, Riverbank Computing Limited
+# All rights reserved.
+#
+# This copy of SIP is licensed for use under the terms of the SIP License
+# Agreement. See the file LICENSE for more details.
+#
+# This copy of SIP may also used under the terms of the GNU General Public
+# License v2 or v3 as published by the Free Software Foundation which can be
+# found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+
+from ...version import SIP_VERSION_STR
+
+from ..specification import (AccessSpecifier, ArgumentType, ArrayArgument,
+ EnumBaseType, IfaceFileType, PySlot)
+from ..utils import append_iface_file, find_method
+
+from .formatters import (ArgumentFormatter, ClassFormatter, format_copying,
+ format_scoped_py_name)
+
+
+def output_pyi(spec, pyi_filename):
+ """ Output a .pyi file. """
+
+ with open(pyi_filename, 'w') as pf:
+ module = spec.modules[0]
+
+ # Write the header.
+ copying = format_copying(module.copying, '#')
+
+ pf.write(
+f'''# The PEP 484 type hints stub file for the {module.py_name} module.
+#
+# Generated by SIP {SIP_VERSION_STR}
+{copying}
+
+
+''')
+
+ if module.is_composite:
+ _composite_module(pf, spec, module)
+ else:
+ _module(pf, spec, module)
+
+
+def _composite_module(pf, spec, module):
+ """ Output the type hints for a composite module. """
+
+ for mod in spec.modules:
+ if mod.composite is module:
+ pf.write(f'from {mod.fq_py_name.name} import *\n')
+
+
+def _module(pf, spec, module):
+ """ Output the type hints for an ordinary module. """
+
+ # Generate the imports. Note that we assume the super-types are the
+ # standard SIP ones.
+ if spec.abi_version >= (13, 0):
+ pf.write('import enum\n')
+
+ pf.write(
+f'''import typing
+
+import {spec.sip_module}
+''')
+
+ imports = []
+
+ for mod in module.imports:
+ parts = mod.fq_py_name.name.split('.')
+
+ if mod.fq_py_name.name == mod.py_name:
+ imports.append('import ' + mod.py_name)
+ else:
+ scope = mod.fq_py_name.name[:-len(mod.py_name) - 1]
+ imports.append('from ' + scope + ' import ' + mod.py_name)
+
+ if imports:
+ pf.write('\n' + '\n'.join(imports) + '\n')
+
+ # Generate any exported type hint code and any module-specific type hint
+ # code.
+ _type_hint_code(pf, spec.exported_type_hint_code)
+ _type_hint_code(pf, module.type_hint_code)
+
+ # Generate the types - global enums must be first.
+ _enums(pf, spec, module)
+
+ # The list of enums and classes that have been defined at any particular
+ # point so we know if they can be referenced directly rather than by their
+ # names as a string.
+ defined = []
+
+ for klass in spec.classes:
+ if klass.iface_file.module is not module:
+ continue
+
+ if klass.external:
+ continue
+
+ if klass.no_type_hint:
+ continue
+
+ # Only handle non-nested classes here.
+ if klass.scope is not None:
+ continue
+
+ # We can't handle extenders.
+ if klass.real_class is not None:
+ continue
+
+ _class(pf, spec, module, klass, defined)
+
+ for mapped_type in spec.mapped_types:
+ if mapped_type.iface_file.module is not module:
+ continue
+
+ if mapped_type.py_name is not None:
+ _mapped_type(pf, spec, module, mapped_type, defined)
+
+ _variables(pf, spec, module, defined)
+
+ first = True
+
+ for member in module.global_functions:
+ if member.py_slot is None:
+ first = _separate(pf, first)
+ _callable(pf, spec, module, member, module.overloads, False,
+ defined)
+
+
+def _type_hint_code(pf, type_hint_code, indent=0):
+ """ Output handwritten type hint code. """
+
+ s = ''
+
+ for block in type_hint_code:
+ s += '\n'
+
+ need_indent = True
+
+ for ch in block.text:
+ if need_indent:
+ s += _indent(indent)
+ need_indent = False
+
+ s += ch
+
+ if ch == '\n':
+ need_indent = True
+
+ pf.write(s)
+
+
+def _class(pf, spec, module, klass, defined, indent=0):
+ """ Output the type hints for a class. """
+
+ nr_overloads = 0
+
+ if not klass.is_hidden_namespace:
+ _separate(pf, True, indent)
+
+ s = _indent(indent)
+
+ s += f'class {klass.py_name.name}('
+
+ if klass.superclasses:
+ s += ', '.join(
+ [ClassFormatter(spec, sc).as_type_hint(module, defined)
+ for sc in klass.superclasses])
+
+ elif klass.supertype is not None:
+ # In ABI v12 the default supertype does not contain the fully
+ # qualified name of the sip module so we fix it here.
+ if spec.abi_version[0] == 12 and klass.supertype.name.startswith('sip.'):
+ s += spec.sip_module + klass.supertype.name[4:]
+ else:
+ s += klass.supertype.name
+
+ else:
+ simple = 'simple' if klass.iface_file.type is IfaceFileType.NAMESPACE else ''
+
+ s += f'{spec.sip_module}.{simple}wrapper'
+
+ # See if there is anything in the class body.
+ for ctor in klass.ctors:
+ if ctor.access_specifier is AccessSpecifier.PRIVATE:
+ continue
+
+ if ctor.no_type_hint:
+ continue
+
+ nr_overloads += 1
+
+ no_body = (klass.type_hint_code is None and nr_overloads == 0)
+
+ if no_body:
+ for overload in klass.overloads:
+ if overload.access_specifier is AccessSpecifier.PRIVATE:
+ continue
+
+ if overload.no_type_hint:
+ continue
+
+ no_body = False
+ break
+
+ if no_body:
+ for enum in spec.enums:
+ if enum.scope is klass and not enum.no_type_hint:
+ no_body = False
+ break
+
+ if no_body:
+ for nested in spec.classes:
+ if nested.scope is klass and not nested.no_type_hint:
+ no_body = False
+ break
+
+ if no_body:
+ for variable in spec.variables:
+ if variable.scope is klass and not variable.no_type_hint:
+ no_body = False
+ break
+
+ suffix = ' ...' if no_body else ''
+
+ s += f'):{suffix}\n'
+
+ pf.write(s)
+
+ indent += 1
+
+ if klass.type_hint_code is not None:
+ _type_hint_code(pf, [klass.type_hint_code], indent)
+
+ _enums(pf, spec, module, defined=defined, scope=klass, indent=indent)
+
+ # Handle any nested classes.
+ for nested in spec.classes:
+ if nested.scope is klass and not nested.no_type_hint:
+ _class(pf, spec, module, nested, defined, indent)
+
+ _variables(pf, spec, module, defined, scope=klass, indent=indent)
+
+ first = True
+
+ for ctor in klass.ctors:
+ if ctor.access_specifier is AccessSpecifier.PRIVATE:
+ continue
+
+ if ctor.no_type_hint:
+ continue
+
+ first = _separate(pf, first, indent)
+
+ _ctor(pf, spec, module, ctor, nr_overloads > 1, defined, indent)
+
+ first = True
+
+ for member in klass.members:
+ first = _separate(pf, first, indent)
+
+ _callable(pf, spec, module, member, klass.overloads,
+ not klass.is_hidden_namespace, defined, indent)
+
+ for prop in klass.properties:
+ first = _separate(pf, first, indent)
+
+ getter = find_method(klass, prop.getter)
+ if getter is not None:
+ _property(pf, spec, module, prop, False, getter, klass.overloads,
+ defined, indent)
+
+ if prop.setter is not None:
+ setter = find_method(klass, prop.setter)
+ if setter is not None:
+ _property(pf, spec, module, prop, True, setter,
+ klass.overloads, defined, indent)
+
+ if not klass.is_hidden_namespace:
+ # Keep track of what has been defined so that forward references are no
+ # longer required.
+ append_iface_file(defined, klass.iface_file)
+
+def _mapped_type(pf, spec, module, mapped_type, defined, indent=0):
+ """ Output the type hints for a mapped type. """
+
+ # See if there is anything in the mapped type body.
+ no_body = (len(mapped_type.members) == 0)
+
+ if no_body:
+ for enum in spec.enums:
+ if enum.scope is mapped_type and not enum.no_type_hint:
+ no_body = FALSE
+ break
+
+ if not no_body:
+ _separate(pf, True, indent)
+
+ s = _indent(indent)
+ s += f'class {mapped_type.py_name.name}({spec.sip_module}.wrapper):\n'
+ pf.write(s)
+
+ indent += 1
+
+ _enums(pf, spec, module, defined=defined, scope=mapped_type,
+ indent=indent)
+
+ first = True
+
+ for member in mapped_type.members:
+ first = _separate(pf, first, indent)
+
+ _callable(pf, spec, module, member, member.overloads, True,
+ defined, indent)
+
+ # Keep track of what has been defined so that forward references are no
+ # longer required.
+ append_iface_file(defined, mapped_type.iface_file)
+
+
+def _ctor(pf, spec, module, ctor, overloaded, defined, indent):
+ """ Output a ctor type hint. """
+
+ if overloaded:
+ s = _indent(indent)
+ s += '@typing.overload\n'
+ pf.write(s)
+
+ s = _indent(indent)
+ s += 'def __init__('
+
+ args = ['self']
+
+ for arg_nr, arg in enumerate(ctor.py_signature.args):
+ as_str = _argument(spec, module, arg, defined, arg_nr=arg_nr)
+ if as_str:
+ args.append(as_str)
+
+ s += ', '.join(args) + ') -> None: ...\n'
+
+ pf.write(s)
+
+
+def _enums(pf, spec, module, defined=None, scope=None, indent=0):
+ """ Output the type hints for all the enums in a scope. """
+
+ for enum in spec.enums:
+ if enum.module is not module:
+ continue
+
+ if enum.scope is not scope:
+ continue
+
+ if enum.no_type_hint:
+ continue
+
+ _separate(pf, True, indent)
+
+ if enum.py_name is not None:
+ enum_type = format_scoped_py_name(enum.scope, enum.py_name.name)
+
+ superclass = 'int'
+
+ if spec.abi_version >= (13, 0):
+ if enum.base_type is EnumBaseType.ENUM:
+ superclass = 'enum.Enum'
+ elif enum.base_type is EnumBaseType.FLAG:
+ superclass = 'enum.Flag'
+ elif enum.base_type in (EnumBaseType.INT_ENUM, EnumBaseType.UNSIGNED_INT_ENUM):
+ superclass = 'enum.IntEnum'
+ elif enum.base_type is EnumBaseType.INT_FLAG:
+ superclass = 'enum.IntFlag'
+
+ s = _indent(indent)
+ s+= f'class {enum.py_name.name}({superclass}):\n'
+ pf.write(s)
+
+ indent += 1
+ else:
+ enum_type = 'int'
+
+ for member in enum.members:
+ if member.no_type_hint:
+ continue
+
+ s = _indent(indent)
+ s += f'{member.py_name.name} = ... # type: {enum_type}\n'
+ pf.write(s)
+
+ if enum.py_name is not None:
+ indent -= 1
+
+
+def _variables(pf, spec, module, defined, scope=None, indent=0):
+ """ Output the type hints for all the variables in a scope. """
+
+ first = True
+
+ for variable in spec.variables:
+ if variable.module is not module:
+ continue
+
+ if variable.scope is not scope:
+ continue
+
+ if variable.no_type_hint:
+ continue
+
+ py_type = _type(spec, module, variable.type, defined)
+
+ first = _separate(pf, first, indent)
+
+ s = _indent(indent)
+ s += f'{variable.py_name.name} = ... # type: {py_type}\n'
+ pf.write(s)
+
+
+def _callable(pf, spec, module, member, overloads, is_method, defined,
+ indent=0):
+ """ Output the type hints for a callable. """
+
+ # Count the number of overloads.
+ nr_overloads = 0
+
+ for overload in overloads:
+ if overload.access_specifier is AccessSpecifier.PRIVATE:
+ continue
+
+ if overload.common is not member:
+ continue
+
+ if overload.no_type_hint:
+ continue
+
+ nr_overloads += 1
+
+ # Handle each overload.
+ overload_nr = 0
+
+ for overload in overloads:
+ if overload.access_specifier is AccessSpecifier.PRIVATE:
+ continue
+
+ if overload.common is not member:
+ continue
+
+ if overload.no_type_hint:
+ continue
+
+ _overload(pf, spec, module, overload, nr_overloads > 1, overload_nr,
+ is_method, defined, indent)
+
+ overload_nr += 1
+
+
+def _property(pf, spec, module, prop, is_setter, member, overloads, defined,
+ indent):
+ """ Output the type hints for a property. """
+
+ for overload in overloads:
+ if overload.access_specifier is AccessSpecifier.PRIVATE:
+ continue
+
+ if overload.common is not member:
+ continue
+
+ if overload.no_type_hint:
+ continue
+
+ s = _indent(indent)
+
+ if is_setter:
+ s += f'@{prop.name.name}.setter\n'
+ else:
+ s += '@property\n'
+
+ pf.write(s)
+
+ signature = _python_signature(spec, module, overload.py_signature,
+ defined)
+
+ s = _indent(indent)
+ s += f'def {prop.name.name}{sinature}: ...\n'
+ pf.write(s)
+
+ break
+
+
+def _overload(pf, spec, module, overload, overloaded, overload_nr, is_method,
+ defined, indent):
+ """ Output the type hints for a single overload. """
+
+ # mypy recommends using 'object' as the argument type.
+ is_eq_slot = (overload.common.py_slot in (PySlot.EQ, PySlot.NE))
+
+ # The recommendation means any subsequent overloads are pointless.
+ if is_eq_slot:
+ if overload_nr > 0:
+ return
+ elif overloaded:
+ pf.write(_indent(indent) + '@typing.overload\n')
+
+ if is_method and overload.is_static:
+ pf.write(_indent(indent) + '@staticmethod\n')
+
+ if is_eq_slot:
+ signature = '(self, other: object)'
+ else:
+ need_self = (is_method and not overload.is_static)
+
+ signature = _python_signature(spec, module, overload.py_signature,
+ defined, need_self=need_self)
+
+ s = _indent(indent)
+ s += f'def {overload.common.py_name.name}{signature}: ...\n'
+ pf.write(s)
+
+
+def _python_signature(spec, module, signature, defined, need_self=True):
+ """ Return a Python signature. """
+
+ # Handle the input values.
+ in_args = []
+
+ if need_self:
+ in_args.append('self')
+
+ nr_out = 0
+
+ for arg_nr, arg in enumerate(signature.args):
+ if arg.is_out:
+ nr_out += 1
+
+ if arg.is_in:
+ as_str = _argument(spec, module, arg, defined, arg_nr=arg_nr)
+ if as_str:
+ in_args.append(as_str)
+
+ in_args = ', '.join(in_args)
+
+ # Handle the output values.
+ result = signature.result
+
+ if result.type is ArgumentType.VOID and len(result.derefs) == 0:
+ is_result = False
+ else:
+ type_hints = result.type_hints
+
+ # An empty type hint specifies a void return.
+ if type_hints is not None and type_hints.hint_out is not None and type_hints.hint_out == '':
+ is_result = False
+ else:
+ is_result = True
+
+ if is_result or nr_out > 0:
+ results_s = ''
+
+ needs_tuple = ((is_result and nr_out > 0) or nr_out > 1)
+
+ if needs_tuple:
+ results_s += 'typing.Tuple['
+
+ out_args = []
+
+ if is_result:
+ as_str = _argument(spec, module, result, defined)
+ if as_str:
+ out_args.append(as_str)
+
+ for arg in signature.args:
+ if arg.is_out:
+ as_str = _argument(spec, module, arg, defined)
+ if as_str:
+ out_args.append(as_str)
+
+ results_s += ', '.join(out_args)
+
+ if needs_tuple:
+ results_s += ']'
+ else:
+ results_s = 'None'
+
+ return f'({in_args}) -> {results_s}'
+
+
+def _argument(spec, module, arg, defined, arg_nr=-1):
+ """ Return a Python argument. """
+
+ if arg.array is ArrayArgument.ARRAY_SIZE:
+ return None
+
+ optional = (arg_nr >= 0 and arg.default_value is not None)
+ use_optional = False
+
+ s = ''
+
+ if arg_nr >= 0 and arg.type is not ArgumentType.ELLIPSIS:
+ if arg.name is None:
+ s += f'a{arg_nr}: '
+ else:
+ name = _fix_py_keyword(arg.name.name)
+ s += f'{name}: '
+
+ if optional:
+ # Assume pointers can be None unless specified otherwise.
+ if arg.allow_none or (not arg.disallow_none and arg.derefs):
+ s += 'typing.Optional['
+ use_optional = True
+
+ if arg.array is ArrayArgument.ARRAY:
+ s += spec.sip_module + '.array['
+
+ s += _type(spec, module, arg, defined, out=(arg_nr < 0))
+
+ if arg_nr >= 0 and arg.type is ArgumentType.ELLIPSIS:
+ if arg.name is None:
+ s += f'a{arg_nr}'
+ else:
+ s += _fix_py_keyword(arg.name.name)
+
+ if arg.array is ArrayArgument.ARRAY:
+ s += ']'
+
+ if optional:
+ if use_optional:
+ s += ']'
+
+ s += ' = ...'
+
+ return s
+
+
+def _type(spec, module, arg, defined, out=False):
+ """ Return the type hint of a type. """
+
+ return ArgumentFormatter(spec, arg).as_type_hint(module, out, defined)
+
+
+def _indent(indent):
+ """ Return the required indentation. """
+
+ return ' ' * (4 * indent)
+
+
+def _separate(pf, first, indent=0):
+ """ Output a newline if not already done. """
+
+ if first:
+ pf.write('\n' if indent else '\n\n')
+
+ return False
+
+
+_PYTHON_KEYWORDS = (
+ 'False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class',
+ 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for',
+ 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not',
+ 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield',
+ 'exec', 'print',
+)
+
+def _fix_py_keyword(word):
+ """ Return a fixed word if it is a Python keyword (or has been at any
+ time).
+ """
+
+ if word in _PYTHON_KEYWORDS:
+ word += '_'
+
+ return word
diff --git a/sipbuild/generator/outputs/type_hints.py b/sipbuild/generator/outputs/type_hints.py
new file mode 100644
index 0000000..9a702dc
--- /dev/null
+++ b/sipbuild/generator/outputs/type_hints.py
@@ -0,0 +1,497 @@
+# Copyright (c) 2022, Riverbank Computing Limited
+# All rights reserved.
+#
+# This copy of SIP is licensed for use under the terms of the SIP License
+# Agreement. See the file LICENSE for more details.
+#
+# This copy of SIP may also used under the terms of the GNU General Public
+# License v2 or v3 as published by the Free Software Foundation which can be
+# found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+
+from copy import copy
+from dataclasses import dataclass, field
+from enum import auto, Enum
+from typing import List, Optional, Union
+from weakref import WeakKeyDictionary
+
+from ...exceptions import UserException
+
+from ..scoped_name import ScopedName
+from ..specification import WrappedClass, WrappedEnum
+
+
+# The types defined in the typing module.
+_TYPING_MODULE = (
+ 'Any', 'NoReturn', 'Tuple', 'Union', 'Optional', 'Callable', 'Type',
+ 'Literal', 'ClassVar', 'Final', 'Annotated', 'AnyStr', 'Protocol',
+ 'NamedTuple', 'Dict', 'List', 'Set', 'FrozenSet', 'IO', 'TextIO',
+ 'BinaryIO', 'Pattern', 'Match', 'Text', 'Iterable', 'Iterator',
+ 'Generator', 'Mapping', 'Sequence',
+)
+
+
+class NodeType(Enum):
+ """ The node types. """
+
+ TYPING = auto()
+ CLASS = auto()
+ ENUM = auto()
+ OTHER = auto()
+
+
+class ParseState(Enum):
+ """ The different parse states of a type hint. """
+
+ REQUIRED = auto()
+ PARSING = auto()
+ PARSED = auto()
+
+
+ at dataclass
+class ManagedTypeHint:
+ """ Encapsulate a managed type hint. """
+
+ # The type hint being managed.
+ type_hint: str
+
+ # The rendered docstring.
+ as_docstring: Optional[str] = None
+
+ # The rendered reST reference.
+ as_rest_ref: Optional[str] = None
+
+ # The parse state.
+ parse_state: ParseState = ParseState.REQUIRED
+
+ # The root node.
+ root: Optional['TypeHintNode'] = None
+
+
+ at dataclass
+class TypeHintNode:
+ """ Encapsulate a node of a parsed type hint. """
+
+ # The type.
+ type: NodeType
+
+ # The list of child nodes.
+ children: Optional[List['TypeHintNode']] = None
+
+ # The type-dependent definition.
+ definition: Optional[Union[str, WrappedClass, WrappedEnum]] = None
+
+ # The next sibling node.
+ next: Optional['TypeHintNode'] = None
+
+
+class TypeHintManager:
+ """ A manager for type hints on behalf of a Specification object. """
+
+ # The map of specification objects and the corresponding manager object.
+ _spec_manager_map = WeakKeyDictionary()
+
+ def __new__(cls, spec):
+ """ Return the existing manager for a specification or create a new one
+ if necessary.
+ """
+
+ try:
+ manager = cls._spec_manager_map[spec]
+ except KeyError:
+ manager = object.__new__(cls)
+
+ manager._spec = spec
+ manager._managed_type_hints = {}
+
+ cls._spec_manager_map[spec] = manager
+
+ return manager
+
+ def as_docstring(self, type_hint, module, out, defined):
+ """ Return the type hint as a docstring. """
+
+ managed_type_hint = self._get_managed_type_hint(type_hint, out)
+
+ # See if it needs rendering.
+ if managed_type_hint.as_docstring is None:
+ managed_type_hint.as_docstring = self._render(managed_type_hint,
+ out, module=module, defined=defined)
+
+ return managed_type_hint.as_docstring
+
+ def as_rest_ref(self, type_hint, out):
+ """ Return the type hint with appropriate reST references. """
+
+ managed_type_hint = self._get_managed_type_hint(type_hint, out)
+
+ # See if it needs rendering.
+ if managed_type_hint.as_rest_ref is None:
+ managed_type_hint.as_rest_ref = self._render(managed_type_hint,
+ out, rest_ref=True)
+
+ return managed_type_hint.as_rest_ref
+
+ def as_type_hint(self, type_hint, module, out, defined):
+ """ Return the type hint as a type hint. """
+
+ managed_type_hint = self._get_managed_type_hint(type_hint, out)
+
+ # Note that we always render type hints as they can be different before
+ # and after a class or enum is defined in the .pyi file.
+ return self._render(managed_type_hint, out, pep484=True,
+ module=module, defined=defined)
+
+ def _get_managed_type_hint(self, type_hint, out):
+ """ Return the unique (for the specification) managed type hint for a
+ type hint.
+ """
+
+ try:
+ hint_in, hint_out = self._managed_type_hints[type_hint]
+ except KeyError:
+ hint_in = ManagedTypeHint(type_hint)
+ hint_out = ManagedTypeHint(type_hint)
+ self._managed_type_hints[type_hint] = (hint_in, hint_out)
+
+ return hint_out if out else hint_in
+
+ def _parse(self, managed_type_hint, out):
+ """ Ensure a type hint has been parsed. """
+
+ if managed_type_hint.parse_state is ParseState.REQUIRED:
+ managed_type_hint.parse_state = ParseState.PARSING
+ managed_type_hint.root = self._parse_node(out,
+ managed_type_hint.type_hint)
+ managed_type_hint.parse_state = ParseState.PARSED
+
+ def _parse_node(self, out, text, start=0, end=None):
+ """ Return a single node of a parsed type hint. """
+
+ if end is None:
+ end = len(text)
+ top_level = True
+ else:
+ top_level = False
+
+ # Find the name and any opening and closing brackets.
+ start = self._strip_leading(text, start, end)
+ name_start = start
+
+ end = self._strip_trailing(text, start, end)
+ name_end = end
+
+ children = None
+
+ i = text[start:end].find('[')
+ if i >= 0:
+ i += start
+
+ children = []
+
+ # The last character must be a closing bracket.
+ if text[end - 1] != ']':
+ raise UserException(
+ f"type hint '{text}': ']' expected at position {end}")
+
+ # Find the end of any name.
+ name_end = self._strip_trailing(text, name_start, i)
+
+ while True:
+ # Skip the opening bracket or comma.
+ i += 1
+
+ # Find the next comma, if any.
+ depth = 0
+
+ for part_i in range(i, end):
+ if text[part_i] == '[':
+ depth += 1
+
+ elif text[part_i] == ']' and depth != 0:
+ depth -= 1
+
+ elif text[part_i] in ',]' and depth == 0:
+ # Recursively parse this part.
+ new_child = self._parse_node(out, text, i, part_i)
+ if new_child is not None:
+ self._append_child(children, new_child)
+
+ i = part_i
+ break
+ else:
+ break
+
+ # See if we have a name.
+ if name_start != name_end:
+ # Get the name. */
+ name = text[name_start:name_end]
+
+ # See if it is an object in the typing module.
+ if name in _TYPING_MODULE:
+ if name == 'Union':
+ # If there are no children assume it is because they have
+ # been omitted.
+ if len(children) == 0:
+ return None
+
+ # Flatten any unions.
+ flattened = []
+
+ for child in children:
+ if child.type is NodeType.TYPING and child.definition == 'Union':
+ for grandchild in child.children:
+ self._append_child(flattened, grandchild)
+ else:
+ self._append_child(flattened, child)
+
+ children = flattened
+
+ node = TypeHintNode(NodeType.TYPING, children=children,
+ definition=name)
+ else:
+ # Search for the type.
+ node = self._lookup_type(name, out)
+ else:
+ # At the top level we must have brackets and they must not be empty.
+ if top_level and (children is None or len(children) == 0):
+ raise UserException(
+ f"type hint '{text}': must have non-empty brackets")
+
+ # Return the representation of brackets.
+ node = TypeHintNode(NodeType.TYPING, children=children)
+
+ return node
+
+ def _render(self, managed_type_hint, out, pep484=False, rest_ref=False,
+ module=None, defined=None):
+ """ Return a rendered type hint. """
+
+ self._parse(managed_type_hint, out)
+
+ if managed_type_hint.root is not None:
+ s = self._render_node(managed_type_hint.root, out, pep484,
+ rest_ref, module, defined)
+ else:
+ s = self._maybe_any_object(managed_type_hint.type_hint,
+ pep484=pep484)
+
+ return s
+
+ def _render_node(self, node, out, pep484, rest_ref, module, defined):
+ """ Render a single node. """
+
+ if node.type is NodeType.TYPING:
+ if node.definition is None:
+ s = ''
+ elif pep484:
+ s = 'typing.' + node.definition
+ else:
+ s = node.definition
+
+ if node.children is not None:
+ children = [self._render_node(c, out, pep484, rest_ref, module,
+ defined) for c in node.children]
+
+ s += '[' + ', '.join(children) + ']'
+
+ elif node.type is NodeType.CLASS:
+ from .formatters import ClassFormatter
+
+ formatter = ClassFormatter(self._spec, node.definition)
+
+ if rest_ref:
+ s = formatter.as_rest_ref()
+ elif pep484:
+ s = formatter.as_type_hint(module, defined)
+ else:
+ s = formatter.fq_py_name
+
+ elif node.type is NodeType.ENUM:
+ from .formatters import EnumFormatter
+
+ formatter = EnumFormatter(self._spec, node.definition)
+
+ if rest_ref:
+ s = formatter.as_rest_ref()
+ elif pep484:
+ s = formatter.as_type_hint(module, defined)
+ else:
+ s = formatter.fq_py_name
+
+ else:
+ s = self._maybe_any_object(node.definition, pep484)
+
+ return s
+
+ @staticmethod
+ def _append_child(children, new_child):
+ """ Append a child to an existing list of children. """
+
+ if len(children) > 1:
+ children[-1].next = new_child
+
+ children.append(new_child)
+
+ def _copy_type_hint(self, type_hint, out):
+ """ Copy the root node of a type hint. """
+
+ managed_type_hint = self._get_managed_type_hint(type_hint, out)
+
+ self._parse(managed_type_hint, out)
+
+ if managed_type_hint.root is None:
+ return None
+
+ node = copy(managed_type_hint.root)
+ node.next = None
+
+ return node
+
+ def _lookup_enum(self, name, scopes):
+ """ Lookup an enum using its C/C++ name. """
+
+ for enum in self._spec.enums:
+ if enum.fq_cpp_name is not None and enum.fq_cpp_name.base_name == name and enum.scope in scopes:
+ return enum
+
+ return None
+
+ def _lookup_class(self, name, scope):
+ """ Lookup a class/struct/union using its C/C++ name. """
+
+ for klass in self._spec.classes:
+ if klass.scope is scope and not klass.external and klass.iface_file.fq_cpp_name.base_name == name:
+ return klass
+
+ return None
+
+ def _lookup_mapped_type(self, name):
+ """ Lookup a mapped type using its C/C++ name. """
+
+ for mapped_type in self._spec.mapped_types:
+ if mapped_type.cpp_name is not None and mapped_type.cpp_name.name == name:
+ return mapped_type
+
+ return None
+
+ def _lookup_type(self, name, out):
+ """ Look up a qualified Python type and return the corresponding node.
+ """
+
+ # Start searching at the global level.
+ scope_klass = None
+ scope_mapped_type = None
+
+ # We allow both Python and C++ scope separators.
+ scoped_name = ScopedName.parse(name.replace('.', '::'))
+
+ for part_i, part in enumerate(scoped_name):
+ is_last_part = ((part_i + 1) == len(scoped_name))
+
+ # See if it's an enum.
+ enum = self._lookup_enum(part, (scope_klass, scope_mapped_type))
+ if enum is not None:
+ if is_last_part:
+ return TypeHintNode(NodeType.ENUM, definition=enum)
+
+ # There is some left so the whole lookup has failed.
+ break
+
+ # If we have a mapped type scope then we must be looking for an
+ # enum, which we have failed to find.
+ if scope_mapped_type is not None:
+ break
+
+ if scope_klass is None:
+ # We are looking at the global level, so see if it is a mapped
+ # type.
+ mapped_type = self._lookup_mapped_type(part)
+ if mapped_type is not None:
+ # If we have used the whole name then the lookup has
+ # succeeded.
+ if is_last_part:
+ if mapped_type.type_hints is not None:
+ type_hint = mapped_type.type_hints.hint_out if out else mapped_type.type_hints.hint_in
+
+ if type_hint is not None:
+ if self._get_managed_type_hint(type_hint, out).parse_state is not ParseState.PARSING:
+ return self._copy_type_hint(type_hint, out)
+
+ return None
+
+ # Otherwise this is the scope for the next part.
+ scope_mapped_type = mapped_type
+
+ if scope_mapped_type is None:
+ # If we get here then it must be a class.
+ klass = self._lookup_class(part, scope_klass)
+ if klass is None:
+ break
+
+ # If we have used the whole name then the lookup has succeeded.
+ if is_last_part:
+ if klass.type_hints is not None:
+ type_hint = klass.type_hints.hint_out if out else klass.type_hints.hint_in
+
+ if type_hint is not None:
+ if self._get_managed_type_hint(type_hint, out).parse_state is not ParseState.PARSING:
+ return self._copy_type_hint(type_hint, out)
+
+ return TypeHintNode(NodeType.CLASS, definition=klass)
+
+ # Otherwise this is the scope for the next part.
+ scope_klass = klass
+
+ # If we have run out of name then the lookup has failed.
+ if is_last_part:
+ break
+
+ # Nothing was found.
+ return TypeHintNode(NodeType.OTHER, definition=name)
+
+ @classmethod
+ def _maybe_any_object(cls, hint, pep484):
+ """ Return a hint taking into account that it may be any sort of
+ object.
+ """
+
+ return hint if hint != 'Any' else cls._any_object(pep484)
+
+ @staticmethod
+ def _any_object(pep484):
+ """ Return a hint taking into account that it may be any sort of
+ object.
+ """
+
+ return 'typing.Any' if pep484 else 'object'
+
+ @staticmethod
+ def _strip_leading(text, start, end):
+ """ Return the index of the first non-space of a string. """
+
+ while start < end and text[start] == ' ':
+ start += 1
+
+ return start
+
+ @staticmethod
+ def _strip_trailing(text, start, end):
+ """ Return the index after the last non-space of a string. """
+
+ while end > start and text[end - 1] == ' ':
+ end -= 1
+
+ return end
diff --git a/sipbuild/generator/outputs/xml.py b/sipbuild/generator/outputs/xml.py
new file mode 100644
index 0000000..c180395
--- /dev/null
+++ b/sipbuild/generator/outputs/xml.py
@@ -0,0 +1,418 @@
+# Copyright (c) 2022, Riverbank Computing Limited
+# All rights reserved.
+#
+# This copy of SIP is licensed for use under the terms of the SIP License
+# Agreement. See the file LICENSE for more details.
+#
+# This copy of SIP may also used under the terms of the GNU General Public
+# License v2 or v3 as published by the Free Software Foundation which can be
+# found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+
+from xml.etree.ElementTree import Element, SubElement
+
+from ..python_slots import is_number_slot
+from ..scoped_name import ScopedName, STRIP_GLOBAL
+from ..specification import (AccessSpecifier, ArgumentType, ArrayArgument,
+ IfaceFileType, KwArgs, PyQtMethodSpecifier, PySlot, Transfer)
+
+from .formatters import (ArgumentFormatter, ClassFormatter, EnumFormatter,
+ format_scoped_py_name, SignatureFormatter, ValueListFormatter,
+ VariableFormatter)
+
+
+# The schema version number.
+_XML_VERSION_NR = '0'
+
+
+def output_xml(spec, module_name):
+ """ Return the root Module element of the XML for a module. """
+
+ # Note that we don't yet handle mapped types, templates or exceptions.
+
+ for module in spec.modules:
+ if module.py_name == module_name:
+ break
+ else:
+ return None
+
+ root = Element('Module', version=_XML_VERSION_NR, name=module.py_name)
+
+ for klass in spec.classes:
+ if klass.iface_file.module is module and not klass.external:
+ _xml_class(root, spec, module, klass)
+
+ for klass in module.proxies:
+ _class(root, spec, module, klass)
+
+ _enums(root, spec, module)
+ _variables(root, spec, module)
+
+ for member in module.global_functions:
+ _function(root, spec, member, module.overloads)
+
+ return root
+
+
+def _realname(scope, member=None):
+ """ Return a 'realname' attribute containing a fully qualified C/C++ name.
+ """
+
+ if scope is None:
+ # The member should have a name in this context.
+ fq_cpp_name = member
+ else:
+ if isinstance(scope, ScopedName):
+ fq_cpp_name = scope
+ else:
+ fq_cpp_name = scope.iface_file.fq_cpp_name
+
+ fq_cpp_name = fq_cpp_name.cpp_stripped(STRIP_GLOBAL)
+
+ if member is not None:
+ fq_cpp_name += '::' + member
+
+ return fq_cpp_name
+
+
+def _class(parent, spec, module, klass):
+ """ Output the XML for a class. """
+
+ if klass.is_opaque:
+ return SubElement(parent, 'OpaqueClass',
+ name=ClassFormatter(spec, klass).fq_py_name)
+
+ if klass.is_hidden_namespace:
+ parent_klass = parent
+ else:
+ attrib = {}
+
+ if klass.pickle_code is not None:
+ attrib['pickle'] = '1'
+
+ if klass.convert_to_type_code is not None:
+ attrib['convert'] = '1'
+
+ if klass.convert_from_type_code is not None:
+ attrib['convertfrom'] = '1'
+
+ if klass.real_class is not None:
+ attrib['extends'] = klass.real_class.iface_file.module.py_name
+
+ if klass.pyqt_flags_enums is not None:
+ attrib['flagsenums'] = ' '.join(klass.pyqt_flags_enums)
+
+ if len(klass.superclasses) != 0:
+ attrib['inherits'] = ' '.join(
+ [ClassFormatter(spec, s).as_rest_ref()
+ for s in klass.superclasses])
+
+ parent_klass = SubElement(parent, 'Class', attrib,
+ name=ClassFormatter(spec, klass).fq_py_name,
+ realname=_realname(klass.iface_file.fq_cpp_name))
+
+ for ctor in klass.ctors:
+ if ctor.access_specifier is not AccessSpecifier.PRIVATE:
+ _ctor(parent_klass, spec, klass, ctor)
+
+ _enums(parent_klass, spec, module, klass)
+ _variables(parent_klass, spec, module, klass)
+
+ for member in klass.members:
+ _function(parent_klass, spec, member, klass.overloads, klass)
+
+
+def _enums(parent, spec, module, scope=None):
+ """ Output the XML for all the enums in a scope. """
+
+ for enum in spec.enums:
+ if enum.module is module and enum.scope is scope:
+ if enum.py_name is None:
+ for member in enum.members:
+ SubElement(parent, 'Member',
+ name=format_scoped_py_name(enum.scope,
+ member.py_name.name),
+ realname=_realname(enum.scope, member.cpp_name),
+ const='1', typename='int')
+ else:
+ enum_el = SubElement(parent, 'Enum',
+ name=format_scoped_py_name(enum.scope,
+ enum.py_name.name),
+ realname=_realname(enum.fq_cpp_name))
+
+ for member in enum.members:
+ name = format_scoped_py_name(enum.scope, enum.py_name.name) + '.' + member.py_name.name
+
+ SubElement(enum_el, 'EnumMember', name=name,
+ realname=_realname(enum.fq_cpp_name,
+ member.cpp_name))
+
+
+def _variables(parent, spec, module, scope=None):
+ """ Output the XML for all the variables in a scope. """
+
+ for variable in spec.variables:
+ if variable.module is module and variable.scope is scope:
+ formatter = VariableFormatter(spec, variable)
+
+ attrib = {}
+
+ if variable.type.is_const or scope is None:
+ attrib['const'] = '1'
+
+ if variable.is_static:
+ attrib['static'] = '1'
+
+ SubElement(parent, 'Member', attrib, name=formatter.fq_py_name,
+ realname=_realname(variable.fq_cpp_name),
+ typename=_typename(spec, variable.type))
+
+
+def _ctor(parent, spec, scope, ctor):
+ """ Output the XML for a ctor. """
+
+ attrib = {}
+
+ if _has_cpp_signature(ctor.cpp_signature):
+ attrib['cppsig'] = _cpp_signature(spec, ctor.cpp_signature)
+
+ function_el = SubElement(parent, 'Function', attrib,
+ name=format_scoped_py_name(scope, '__init__'),
+ realname=_realname(scope, '__init__'))
+
+ for arg in ctor.py_signature.args:
+ if arg.is_in:
+ _argument(function_el, spec, arg, ctor.kw_args)
+
+ if arg.is_out:
+ _argument(function_el, spec, arg, ctor.kw_args, out=True)
+
+
+def _function(parent, spec, member, overloads, scope=None):
+ """ Output the XML for a function. """
+
+ for overload in overloads:
+ if overload.common is member and overload.access_specifier is not AccessSpecifier.PRIVATE:
+ if overload.pyqt_method_specifier is PyQtMethodSpecifier.SIGNAL:
+ attrib = {}
+
+ if _has_cpp_signature(overload.cpp_signature):
+ attrib['cppsig'] = _cpp_signature(spec,
+ overload.cpp_signature)
+
+ signal_el = SubElement(parent, 'Signal', attrib,
+ name=format_scoped_py_name(scope, member.py_name.name),
+ realname=_realname(scope, overload.cpp_name))
+
+ for arg in overload.py_signature.args:
+ _argument(signal_el, spec, arg, overload.kw_args)
+ else:
+ extends = None
+ is_static = (scope is None or scope.iface_file.type is IfaceFileType.NAMESPACE or overload.is_static)
+
+ if scope is None and member.py_slot is not None and overload.py_signature.args[0].type is ArgumentType.CLASS:
+ extends = overload.py_signature.args[0].definition
+ is_static = False
+
+ _overload(parent, spec, scope, overload, extends,
+ is_static)
+
+
+def _overload(parent, spec, scope, overload, extends, is_static):
+ """ Output the XML for an overload. """
+
+ if overload.is_reflected:
+ name = _reflected_slot(overload.common.py_slot)
+ else:
+ name = None
+
+ if name is None:
+ name = overload.common.py_name.name
+ cpp_name = overload.cpp_name
+ else:
+ cpp_name = name
+
+ attrib = {}
+
+ if _has_cpp_signature(overload.cpp_signature):
+ attrib['cppsig'] = _cpp_signature(spec, overload.cpp_signature,
+ is_const=overload.is_const)
+
+ if overload.is_abstract:
+ attrib['abstract'] = '1'
+
+ if is_static:
+ attrib['static'] = '1'
+
+ if overload.pyqt_method_specifier is PyQtMethodSpecifier.SLOT:
+ attrib['slot'] = '1'
+
+ if overload.is_virtual:
+ attrib['virtual'] = '1'
+
+ if extends is not None:
+ attrib['extends'] = ClassFormatter(spec, extends).fq_py_name
+
+ function_el = SubElement(parent, 'Function', attrib,
+ name=format_scoped_py_name(scope, name),
+ realname=_realname(scope, cpp_name))
+
+ # An empty type hint specifies a void return.
+ result = overload.py_signature.result
+
+ if result.type_hints is not None and result.type_hints.hint_out is not None and result.type_hints.hint_out == '':
+ no_result = True
+ else:
+ no_result = (result.type is ArgumentType.VOID and len(result.derefs) == 0)
+
+ if not no_result:
+ _argument(function_el, spec, overload.py_signature.result, KwArgs.NONE,
+ out=True,
+ transfer_result=overload.transfer is Transfer.TRANSFER_BACK)
+
+ # Ignore the first argument of non-reflected number slots and the second
+ # argument of reflected number slots.
+ might_ignore = (is_number_slot(overload.common.py_slot) and len(overload.py_signature.args) == 2)
+
+ for a, arg in enumerate(overload.py_signature.args):
+ if might_ignore:
+ if a == 0 and not overload.is_reflected:
+ continue
+
+ if a == 1 and overload.is_reflected:
+ continue
+
+ if arg.is_in:
+ _argument(function_el, spec, arg, overload.kw_args)
+
+ if arg.is_out:
+ _argument(function_el, spec, arg, overload.kw_args, out=True)
+
+
+# Argument types that imply handwritten code.
+_HANDWRITTEN_CODE_TYPES = (
+ ArgumentType.PYOBJECT,
+ ArgumentType.PYTUPLE,
+ ArgumentType.PYLIST,
+ ArgumentType.PYDICT,
+ ArgumentType.PYCALLABLE,
+ ArgumentType.PYSLICE,
+ ArgumentType.PYTYPE,
+ ArgumentType.PYBUFFER,
+ ArgumentType.PYENUM,
+ ArgumentType.CAPSULE,
+)
+
+def _has_cpp_signature(signature):
+ """ Return True if there is a C/C++ signature. """
+
+ if signature is None:
+ return False
+
+ # See if there are any arguments that could only have come from handwritten
+ # code.
+ for arg in signature.args:
+ if arg.type in _HANDWRITTEN_CODE_TYPES:
+ return False
+
+ return True
+
+
+def _cpp_signature(spec, signature, is_const=False):
+ """ Return the XML for a C++ signature. """
+
+ formatter = SignatureFormatter(spec, signature)
+
+ args = formatter.cpp_arguments(strip=STRIP_GLOBAL, make_public=True,
+ as_xml=True)
+ const = ' const' if is_const else ''
+
+ return f'({args}){const}'
+
+
+def _argument(parent, spec, arg, kw_args, out=False,
+ transfer_result=False):
+ """ Ouput the XML for an argument. """
+
+ if arg.array is not ArrayArgument.ARRAY_SIZE:
+ attrib = {}
+
+ if not out:
+ if arg.allow_none:
+ attrib['allownone'] = '1'
+
+ if arg.disallow_none:
+ attrib['disallownone'] = '1'
+
+ if arg.transfer is Transfer.TRANSFER:
+ attrib['transfer'] = 'to'
+ elif arg.transfer is Transfer.TRANSFER_THIS:
+ attrib['transfer'] = 'this'
+
+ if transfer_result or arg.transfer is Transfer.TRANSFER_BACK:
+ attrib['transfer'] = 'back'
+
+ SubElement(parent, 'Return' if out else 'Argument', attrib,
+ typename=_typename(spec, arg, kw_args=kw_args, out=out))
+
+
+def _typename(spec, arg, kw_args=KwArgs.NONE, out=False):
+ """ Return the XML for a type. """
+
+ s = ''
+
+ # Handle the argument name.
+ if not out and arg.name is not None:
+ if kw_args is KwArgs.ALL or (kw_args is KwArgs.OPTIONAL and arg.default_value is not None):
+ s += arg.name.name + ': '
+
+ s += ArgumentFormatter(spec, arg).as_rest_ref(out)
+
+ if not out and arg.name is not None and arg.default_value is not None:
+ s += ' = '
+
+ # Try and convert the value to a reST reference. We don't try very
+ # hard but will get most cases.
+ rest_ref = ValueListFormatter(spec, arg.default_value).as_rest_ref()
+ if rest_ref is None:
+ rest_ref = formatter.py_default_value()
+
+ s += rest_ref
+
+ return s
+
+
+# A map of slots and the names of their reflections.
+_SLOT_REFLECTIONS = {
+ PySlot.ADD: '__radd__',
+ PySlot.SUB: '__rsub__',
+ PySlot.MUL: '__rmul__',
+ PySlot.MATMUL: '__rmatmul__',
+ PySlot.TRUEDIV: '__rtruediv__',
+ PySlot.FLOORDIV: '__rfloordiv__',
+ PySlot.MOD: '__rmod__',
+ PySlot.LSHIFT: '__rlshift__',
+ PySlot.RSHIFT: '__rrshift__',
+ PySlot.AND: '__rand__',
+ PySlot.OR: '__ror__',
+ PySlot.XOR: '__rxor__',
+}
+
+def _reflected_slot(py_slot):
+ """ Return the name of the reflected version of a slot or None if it
+ doesn't have one.
+ """
+
+ return _SLOT_REFLECTIONS.get(py_slot)
diff --git a/sipbuild/generator/parser/parser.py b/sipbuild/generator/parser/parser.py
index 66db745..54e613f 100644
--- a/sipbuild/generator/parser/parser.py
+++ b/sipbuild/generator/parser/parser.py
@@ -25,11 +25,13 @@ from .parser_manager import ParserManager
def parse(sip_file, hex_version, encoding, abi_version, tags,
- disabled_features, protected_is_public, include_dirs, strict=True):
+ disabled_features, protected_is_public, include_dirs, sip_module,
+ is_strict=True):
""" Parse a .sip specification file returning a corresponding Specification
object and a list of the .sip files that define the module to be generated.
"""
return ParserManager(
hex_version, encoding, abi_version, tags, disabled_features,
- protected_is_public, include_dirs, strict).parse(sip_file)
+ protected_is_public, include_dirs, sip_module, is_strict).parse(
+ sip_file)
diff --git a/sipbuild/generator/parser/parser_manager.py b/sipbuild/generator/parser/parser_manager.py
index ed00e43..b55c5f6 100644
--- a/sipbuild/generator/parser/parser_manager.py
+++ b/sipbuild/generator/parser/parser_manager.py
@@ -41,7 +41,6 @@ from ..specification import (AccessSpecifier, Argument, ArgumentType,
Specification, Transfer, TypeHints, WrappedClass, WrappedException,
WrappedEnum, WrappedEnumMember)
from ..templates import encoded_template_name, same_template_signature
-from ..type_hints import get_type_hint
from ..utils import (argument_as_str, cached_name, find_iface_file,
normalised_scoped_name, same_base_type)
@@ -56,7 +55,8 @@ class ParserManager:
"""
def __init__(self, hex_version, encoding, abi_version, tags,
- disabled_features, protected_is_public, include_dirs, strict):
+ disabled_features, protected_is_public, include_dirs, sip_module,
+ is_strict):
""" Initialise the manager. """
# Create the lexer.
@@ -78,7 +78,8 @@ class ParserManager:
self.tags = tags
self.spec = Specification(
- tuple([int(v) for v in abi_version.split('.')]), strict)
+ tuple([int(v) for v in abi_version.split('.')]), is_strict,
+ sip_module)
self.c_bindings = None
self.code_block = None
@@ -1418,31 +1419,27 @@ class ParserManager:
None if none were specified.
"""
- th_text = annotations.get('TypeHint')
- th_in_text = annotations.get('TypeHintIn')
- th_out_text = annotations.get('TypeHintOut')
+ th = annotations.get('TypeHint')
+ th_in = annotations.get('TypeHintIn')
+ th_out = annotations.get('TypeHintOut')
th_value = annotations.get('TypeHintValue')
- if th_in_text is None:
- th_in_text = th_text
- elif th_text is not None:
+ if th_in is None:
+ th_in = th
+ elif th is not None:
self.parser_error(p, symbol,
"'TypeHint' and 'TypeHintIn' cannot both be specified")
return None
- th_in = None if th_in_text is None else get_type_hint(self.spec, th_in_text)
-
- if th_out_text is None:
- th_out_text = th_text
- elif th_text is not None:
+ if th_out is None:
+ th_out = th
+ elif th is not None:
self.parser_error(p, symbol,
"'TypeHint' and 'TypeHintOut' cannot both be specified")
return None
- th_out = None if th_out_text is None else get_type_hint(self.spec, th_out_text)
-
if th_in is not None or th_out is not None or th_value is not None:
# Check that type hints haven't been suppressed.
if annotations.get('NoTypeHint') is not None:
diff --git a/sipbuild/generator/resolver/resolver.py b/sipbuild/generator/resolver/resolver.py
index 6585113..6097a78 100644
--- a/sipbuild/generator/resolver/resolver.py
+++ b/sipbuild/generator/resolver/resolver.py
@@ -601,7 +601,7 @@ def _move_global_slot(spec, mod, global_slot, error_log):
# Move the overload to the end of the destination list.
if is_second:
- overload.is_reflected
+ overload.is_reflected = True
overload.access_specifier = AccessSpecifier.PUBLIC
overload.common = arg_member
@@ -2190,9 +2190,10 @@ def _check_properties(klass, error_log):
def _log_overload_error(error_log, text, overload, scope=None):
""" Log an error about an overload. """
- from ..formatters import OverloadFormatter
-
- formatter = OverloadFormatter(overload, scope)
+ if scope is None:
+ fq_cpp_name = overload.cpp_name
+ else:
+ fq_cpp_name = f'{scope.iface_file.fq_cpp_name}::{overload.cpp_name}'
- error_log.log(f"'{formatter.fq_cpp_name}' {text}",
+ error_log.log(f"'{fq_cpp_name}' {text}",
source_location=overload.source_location)
diff --git a/sipbuild/generator/specification.py b/sipbuild/generator/specification.py
index 4c8ea86..62f711d 100644
--- a/sipbuild/generator/specification.py
+++ b/sipbuild/generator/specification.py
@@ -1289,6 +1289,9 @@ class Specification:
# Set if the specification is strict.
is_strict: bool
+ # The name of the sip module.
+ sip_module: str
+
# Set if the bindings are for C rather than C++.
c_bindings: bool = False
@@ -1374,23 +1377,15 @@ class ThrowArguments:
arguments: Optional[List['WrappedException']] = None
- at dataclass
-class TypeHint:
- """ Encapsulate a PEP 484 type hint. """
-
- # The text of the hint.
- text: str
-
-
@dataclass
class TypeHints:
""" Encapsulate a set of PEP 484 type hints for a type. """
# The type hint when used to pass a value into a callable.
- hint_in: Optional[TypeHint]
+ hint_in: Optional[str]
# The type hint used to return a value from a callable.
- hint_out: Optional[TypeHint]
+ hint_out: Optional[str]
# The representation of a default value in a type hint.
default_value: Optional[str]
diff --git a/sipbuild/generator/type_hints.py b/sipbuild/generator/type_hints.py
deleted file mode 100644
index 872b6be..0000000
--- a/sipbuild/generator/type_hints.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright (c) 2022, Riverbank Computing Limited
-# All rights reserved.
-#
-# This copy of SIP is licensed for use under the terms of the SIP License
-# Agreement. See the file LICENSE for more details.
-#
-# This copy of SIP may also used under the terms of the GNU General Public
-# License v2 or v3 as published by the Free Software Foundation which can be
-# found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-
-
-from weakref import WeakKeyDictionary
-
-from .specification import TypeHint
-
-
-class TypeHintManager:
- """ A manager for type hints on behalf of a Specification object. """
-
- # The map of specification objects and the corresponding manager object.
- _spec_manager_map = WeakKeyDictionary()
-
- def __new__(cls, spec):
- """ Return the existing manager for a specification or create a new one
- if necessary.
- """
-
- try:
- manager = cls._spec_manager_map[spec]
- except KeyError:
- manager = object.__new__(cls)
- cls._spec_manager_map[spec] = manager
-
- return manager
-
- def __init__(self, spec):
- """ Initialise the manager. """
-
- self._type_hints = {}
-
- def get_type_hint(self, text):
- """ Return a unique (to the specification) TypeHint object for the text
- of a hint.
- """
-
- try:
- type_hint = self._type_hints[text]
- except KeyError:
- type_hint = TypeHint(text)
- self._type_hints[text] = type_hint
-
- return type_hint
-
-
-def get_type_hint(spec, text):
- """ Return a unique TypeHint object for the text of a hint. """
-
- return TypeHintManager(spec).get_type_hint(text)
diff --git a/sipbuild/version.py b/sipbuild/version.py
index e31c7a6..918f304 100644
--- a/sipbuild/version.py
+++ b/sipbuild/version.py
@@ -1,2 +1,2 @@
-SIP_VERSION = 0x060702
-SIP_VERSION_STR = '6.7.2'
+SIP_VERSION = 0x060703
+SIP_VERSION_STR = '6.7.3'
More information about the Neon-commits
mailing list