Skip to content

Commit

Permalink
imprv: Generator: Add deprecations
Browse files Browse the repository at this point in the history
  • Loading branch information
matperc committed Mar 3, 2024
1 parent d2c7dbd commit 18fdc55
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 9 deletions.
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ authors = [
{email = "[email protected]"},
{name = "Christoph Reiter"}
]
dependencies = [
# for @deprecated decorator
"typing_extensions>=4.5.0; python_version<'3.13'",
]
classifiers = [
"Programming Language :: Python :: 3",
"Intended Audience :: Developers",
Expand Down Expand Up @@ -50,7 +54,8 @@ include = '\.pyi?$'
skip = "*__pycache__*,.mypy_cache,.git,test,*.pyi"
ignore-words-list = """
astroid,
inout"""
inout,
gir"""

[tool.isort]
force_alphabetical_sort_within_sections = true
Expand Down
72 changes: 64 additions & 8 deletions tools/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import gi
import gi._gi as GI
import gir
import parse

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

ObjectT = Union[ModuleType, Type[Any]]

DEPRECATION_DOCS: dict[str, str] = {}


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

typevars: list[str] = []
imports: list[str] = []
imports: list[str] = [
"""
try:
from warnings import deprecated
except ImportError:
from typing_extensions import deprecated
"""
]
if "cairo" in ns:
imports = ["import cairo"]
imports += ["import cairo"]
typevars.append('_SomeSurface = TypeVar("_SomeSurface", bound=cairo.Surface)')
ns.remove("cairo")

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


def _check_deprecation(obj: Any, full_name: str, default_message: str) -> str:
ret = ""
if hasattr(obj, "is_deprecated"):
if obj.is_deprecated():
message = (
# Currently not implemented:
# https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/80
(
obj.get_attribute("deprecated")
if hasattr(obj, "get_attribute")
else None
)
or (
DEPRECATION_DOCS[full_name]
if full_name in DEPRECATION_DOCS
else None
)
or default_message
)
ret += f'@deprecated("{message}")\n'
return ret


def _gi_build_stub(
parent: ObjectT,
current_namespace: str,
Expand Down Expand Up @@ -601,6 +634,12 @@ def _gi_build_stub(

# Functions
for name in sorted(functions):
full_name = _generate_full_name(prefix_name, name)
ret += _check_deprecation(
functions[name],
full_name,
f"This {'method' if in_class else 'function'} is deprecated",
)
override = _check_override(prefix_name, name, overrides)
if override:
ret += override + "\n"
Expand All @@ -615,13 +654,18 @@ def _gi_build_stub(

# Classes
for name, obj in sorted(classes.items()):
full_name = _generate_full_name(prefix_name, name)

if hasattr(obj, "__info__"):
ret += _check_deprecation(
obj.__info__, full_name, "This class is deprecated"
)

override = _check_override(prefix_name, name, overrides)
if override:
ret += override + "\n\n"
continue

full_name = _generate_full_name(prefix_name, name)

classret = _gi_build_stub(
obj,
current_namespace,
Expand Down Expand Up @@ -804,13 +848,18 @@ def _gi_build_stub(

# Flags
for name, obj in sorted(flags.items()):
full_name = _generate_full_name(prefix_name, name)

if hasattr(obj, "__info__"):
ret += _check_deprecation(
obj.__info__, full_name, "This class is deprecated"
)

override = _check_override(prefix_name, name, overrides)
if override:
ret += override + "\n\n"
continue

full_name = _generate_full_name(prefix_name, name)

if current_namespace == "GObject":
if name != "GFlags":
base = "GFlags"
Expand Down Expand Up @@ -847,13 +896,18 @@ def _gi_build_stub(

# Enums
for name, obj in sorted(enums.items()):
full_name = _generate_full_name(prefix_name, name)

if hasattr(obj, "__info__"):
ret += _check_deprecation(
obj.__info__, full_name, "This class is deprecated"
)

override = _check_override(prefix_name, name, overrides)
if override:
ret += override + "\n\n"
continue

full_name = _generate_full_name(prefix_name, name)

if current_namespace == "GObject":
if name != "GEnum":
base = "GEnum"
Expand Down Expand Up @@ -940,6 +994,8 @@ def start(module: str, version: str, overrides: dict[str, str]) -> str:

args = parser.parse_args()

DEPRECATION_DOCS = gir.load_gir(args.module, args.version)

if args.output:
overrides: dict[str, str] = {}
try:
Expand Down
63 changes: 63 additions & 0 deletions tools/gir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from typing import cast

import os
import re
import sys
import xml.etree.ElementTree as ET

import gi
from gi.repository import GLib


def _get_gir_path(girname: str) -> str:
searchdirs: list[str] = []

from_env = os.getenv("GI_GIR_PATH", "")
if from_env:
searchdirs.extend(from_env.split(os.pathsep))

user_data_dir = GLib.get_user_data_dir()
if user_data_dir is not None:
searchdirs.append(os.path.join(user_data_dir, "gir-1.0"))

for path in GLib.get_system_data_dirs():
searchdirs.append(os.path.join(path, "gir-1.0"))

if os.name != "nt":
# For backwards compatibility, was always unconditionally added to the list.
searchdirs.append("/usr/share/gir-1.0")

for d in searchdirs:
path = os.path.join(d, girname)
if os.path.exists(path):
return path

sys.stderr.write(f"Couldn't find '{girname}' (search path: '{searchdirs}')\n")
sys.exit(1)


def load_gir(module: str, version: str) -> dict[str, str]:
deprecation_docs: dict[str, str] = {}
ns = {
"core": "http://www.gtk.org/introspection/core/1.0",
"c": "http://www.gtk.org/introspection/c/1.0",
"glib": "http://www.gtk.org/introspection/glib/1.0",
}
gir_tree = ET.parse(_get_gir_path(f"{module}-{version}.gir"))
gir_root = gir_tree.getroot()
gir_parent_map = {c: p for p in gir_tree.iter() for c in p}

for child in gir_root.iterfind(".//core:doc-deprecated", ns):
parents: list[str] = []
parent = gir_parent_map[child]
while True:
try:
parents.insert(0, parent.attrib["name"])
except KeyError:
break
parent = gir_parent_map[parent]
deprecation_docs[".".join(parents[1:])] = re.sub(
" +", " ", cast(str, child.text).replace("\n", " ").replace('"', '\\"')
)

return deprecation_docs

0 comments on commit 18fdc55

Please sign in to comment.