Skip to content

Commit a116bcd

Browse files
matpercjtojnar
authored andcommitted
imprv: Generator: Add deprecations
1 parent b519ad0 commit a116bcd

File tree

3 files changed

+133
-9
lines changed

3 files changed

+133
-9
lines changed

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ authors = [
1414
{email = "[email protected]"},
1515
{name = "Christoph Reiter"}
1616
]
17+
dependencies = [
18+
# for @deprecated decorator
19+
'typing_extensions>="4.5.0"; python_version<"3.13"',
20+
]
1721
classifiers = [
1822
"Programming Language :: Python :: 3",
1923
"Intended Audience :: Developers",
@@ -50,7 +54,8 @@ include = '\.pyi?$'
5054
skip = "*__pycache__*,.mypy_cache,.git,test,*.pyi"
5155
ignore-words-list = """
5256
astroid,
53-
inout"""
57+
inout,
58+
gir"""
5459

5560
[tool.isort]
5661
force_alphabetical_sort_within_sections = true

tools/generate.py

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import gi
2626
import gi._gi as GI
27+
import gir
2728
import parse
2829

2930
gi.require_version("GIRepository", "2.0")
@@ -34,6 +35,8 @@
3435

3536
ObjectT = Union[ModuleType, Type[Any]]
3637

38+
DEPRECATION_DOCS: dict[str, str] = {}
39+
3740

3841
def _object_get_props(
3942
obj: GI.ObjectInfo,
@@ -339,9 +342,16 @@ def _build(parent: ObjectT, namespace: str, overrides: dict[str, str]) -> str:
339342
typings = "from typing import Any, Callable, Literal, Optional, Tuple, Type, TypeVar, Sequence"
340343

341344
typevars: list[str] = []
342-
imports: list[str] = []
345+
imports: list[str] = [
346+
"""
347+
try:
348+
from warnings import deprecated
349+
except ImportError:
350+
from typing_extensions import deprecated
351+
"""
352+
]
343353
if "cairo" in ns:
344-
imports = ["import cairo"]
354+
imports += ["import cairo"]
345355
typevars.append('_SomeSurface = TypeVar("_SomeSurface", bound=cairo.Surface)')
346356
ns.remove("cairo")
347357

@@ -507,6 +517,29 @@ def _check_override(prefix: str, name: str, overrides: dict[str, str]) -> Option
507517
return None
508518

509519

520+
def _check_deprecation(obj: Any, full_name: str, default_message: str) -> str:
521+
ret = ""
522+
if hasattr(obj, "is_deprecated"):
523+
if obj.is_deprecated():
524+
message = (
525+
# Currently not implemented:
526+
# https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/80
527+
(
528+
obj.get_attribute("deprecated")
529+
if hasattr(obj, "get_attribute")
530+
else None
531+
)
532+
or (
533+
DEPRECATION_DOCS[full_name]
534+
if full_name in DEPRECATION_DOCS
535+
else None
536+
)
537+
or default_message
538+
)
539+
ret += f'@deprecated("{message}")\n'
540+
return ret
541+
542+
510543
def _gi_build_stub(
511544
parent: ObjectT,
512545
current_namespace: str,
@@ -601,6 +634,12 @@ def _gi_build_stub(
601634

602635
# Functions
603636
for name in sorted(functions):
637+
full_name = _generate_full_name(prefix_name, name)
638+
ret += _check_deprecation(
639+
functions[name],
640+
full_name,
641+
f"This {'method' if in_class else 'function'} is deprecated",
642+
)
604643
override = _check_override(prefix_name, name, overrides)
605644
if override:
606645
ret += override + "\n"
@@ -615,13 +654,18 @@ def _gi_build_stub(
615654

616655
# Classes
617656
for name, obj in sorted(classes.items()):
657+
full_name = _generate_full_name(prefix_name, name)
658+
659+
if hasattr(obj, "__info__"):
660+
ret += _check_deprecation(
661+
obj.__info__, full_name, "This class is deprecated"
662+
)
663+
618664
override = _check_override(prefix_name, name, overrides)
619665
if override:
620666
ret += override + "\n\n"
621667
continue
622668

623-
full_name = _generate_full_name(prefix_name, name)
624-
625669
classret = _gi_build_stub(
626670
obj,
627671
current_namespace,
@@ -804,13 +848,18 @@ def _gi_build_stub(
804848

805849
# Flags
806850
for name, obj in sorted(flags.items()):
851+
full_name = _generate_full_name(prefix_name, name)
852+
853+
if hasattr(obj, "__info__"):
854+
ret += _check_deprecation(
855+
obj.__info__, full_name, "This class is deprecated"
856+
)
857+
807858
override = _check_override(prefix_name, name, overrides)
808859
if override:
809860
ret += override + "\n\n"
810861
continue
811862

812-
full_name = _generate_full_name(prefix_name, name)
813-
814863
if current_namespace == "GObject":
815864
if name != "GFlags":
816865
base = "GFlags"
@@ -847,13 +896,18 @@ def _gi_build_stub(
847896

848897
# Enums
849898
for name, obj in sorted(enums.items()):
899+
full_name = _generate_full_name(prefix_name, name)
900+
901+
if hasattr(obj, "__info__"):
902+
ret += _check_deprecation(
903+
obj.__info__, full_name, "This class is deprecated"
904+
)
905+
850906
override = _check_override(prefix_name, name, overrides)
851907
if override:
852908
ret += override + "\n\n"
853909
continue
854910

855-
full_name = _generate_full_name(prefix_name, name)
856-
857911
if current_namespace == "GObject":
858912
if name != "GEnum":
859913
base = "GEnum"
@@ -940,6 +994,8 @@ def start(module: str, version: str, overrides: dict[str, str]) -> str:
940994

941995
args = parser.parse_args()
942996

997+
DEPRECATION_DOCS = gir.load_gir(args.module, args.version)
998+
943999
if args.output:
9441000
overrides: dict[str, str] = {}
9451001
try:

tools/gir.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from typing import cast
2+
3+
import os
4+
import re
5+
import sys
6+
import xml.etree.ElementTree as ET
7+
8+
import gi
9+
from gi.repository import GLib
10+
11+
12+
def _get_gir_path(girname: str) -> str:
13+
searchdirs: list[str] = []
14+
15+
from_env = os.getenv("GI_GIR_PATH", "")
16+
if from_env:
17+
searchdirs.extend(from_env.split(os.pathsep))
18+
19+
user_data_dir = GLib.get_user_data_dir()
20+
if user_data_dir is not None:
21+
searchdirs.append(os.path.join(user_data_dir, "gir-1.0"))
22+
23+
for path in GLib.get_system_data_dirs():
24+
searchdirs.append(os.path.join(path, "gir-1.0"))
25+
26+
if os.name != "nt":
27+
# For backwards compatibility, was always unconditionally added to the list.
28+
searchdirs.append("/usr/share/gir-1.0")
29+
30+
for d in searchdirs:
31+
path = os.path.join(d, girname)
32+
if os.path.exists(path):
33+
return path
34+
35+
sys.stderr.write(f"Couldn't find '{girname}' (search path: '{searchdirs}')\n")
36+
sys.exit(1)
37+
38+
39+
def load_gir(module: str, version: str) -> dict[str, str]:
40+
deprecation_docs: dict[str, str] = {}
41+
ns = {
42+
"core": "http://www.gtk.org/introspection/core/1.0",
43+
"c": "http://www.gtk.org/introspection/c/1.0",
44+
"glib": "http://www.gtk.org/introspection/glib/1.0",
45+
}
46+
gir_tree = ET.parse(_get_gir_path(f"{module}-{version}.gir"))
47+
gir_root = gir_tree.getroot()
48+
gir_parent_map = {c: p for p in gir_tree.iter() for c in p}
49+
50+
for child in gir_root.iterfind(".//core:doc-deprecated", ns):
51+
parents: list[str] = []
52+
parent = gir_parent_map[child]
53+
while True:
54+
try:
55+
parents.insert(0, parent.attrib["name"])
56+
except KeyError:
57+
break
58+
parent = gir_parent_map[parent]
59+
deprecation_docs[".".join(parents[1:])] = re.sub(
60+
" +", " ", cast(str, child.text).replace("\n", " ").replace('"', '\\"')
61+
)
62+
63+
return deprecation_docs

0 commit comments

Comments
 (0)