This patch adds type hints to the Python classes.
Same as previously done in GLFileTable that I wrote. The only new
thing introduced is the syntax for class variables, so this line in a
class definition:
section_label_pattern = re.compile(...)
becomes this:
section_label_pattern: ClassVar[re.Pattern] = re.compile(...)
That variable is shared between all GLModule objects to match the
start of sections in the module description (e.g. 'Depends-on:').
I guess it is also used for checkers to give warnings when modifying
class variables on an instance variable:
var = GLModule(...)
var.section_label_pattern = 'warning here'
GLModule.section_label_pattern = 'no warning here'
But I haven't checked that. I remember finding those confusing when I
first used Python, so I think it serves as a good reminder. No harm as
far as comparability goes since it was introduced in version 3.5 [1].
I'll probably wait until late today or tomorrow to push this + the
--create-megatestdir fix [2].
[1] https://docs.python.org/3/library/typing.html#typing.ClassVarx
[2] https://lists.gnu.org/archive/html/bug-gnulib/2024-04/msg00501.html
Collin
From 613a0c35a7a03d1c716eba8ae98d5018f04a57f5 Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.fu...@gmail.com>
Date: Mon, 29 Apr 2024 03:25:32 -0700
Subject: [PATCH] gnulib-tool.py: Add type hints to classes.
* pygnulib/*.py: Add type hints for all instance and class variables.
* pygnulib/GLMakefileTable.py (GLMakefileTable.__getitem__): Fix return
type hint since the dictionary has str values.
---
ChangeLog | 7 +++++++
pygnulib/GLConfig.py | 2 ++
pygnulib/GLEmiter.py | 3 +++
pygnulib/GLError.py | 7 ++++++-
pygnulib/GLFileSystem.py | 9 +++++++++
pygnulib/GLImport.py | 8 ++++++++
pygnulib/GLMakefileTable.py | 5 ++++-
pygnulib/GLModuleSystem.py | 33 +++++++++++++++++++++++++++++++--
pygnulib/GLTestDir.py | 12 ++++++++++++
9 files changed, 82 insertions(+), 4 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index c52dee0b6f..8472be20ee 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2024-04-29 Collin Funk <collin.fu...@gmail.com>
+
+ gnulib-tool.py: Add type hints to classes.
+ * pygnulib/*.py: Add type hints for all instance and class variables.
+ * pygnulib/GLMakefileTable.py (GLMakefileTable.__getitem__): Fix return
+ type hint since the dictionary has str values.
+
2024-04-28 Collin Funk <collin.fu...@gmail.com>
doc: Update macro list in gnulib-cache.m4 documentation.
diff --git a/pygnulib/GLConfig.py b/pygnulib/GLConfig.py
index 16fa490fc6..92aa49d700 100644
--- a/pygnulib/GLConfig.py
+++ b/pygnulib/GLConfig.py
@@ -41,6 +41,8 @@ class GLConfig:
By default all attributes are set to empty string, empty list or zero.
The most common value, however, is a None value.'''
+ table: dict[str, Any]
+
def __init__(self,
destdir: str | None = None,
localpath: list[str] | None = None,
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index ed6eae4997..3fbf796aaa 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -98,6 +98,9 @@ def _eliminate_NMD(snippet: str, automake_subdir: bool) -> str:
class GLEmiter:
'''This class is used to emit the contents of necessary files.'''
+ info: GLInfo
+ config: GLConfig
+
def __init__(self, config: GLConfig) -> None:
'''Create GLEmiter instance.'''
self.info = GLInfo()
diff --git a/pygnulib/GLError.py b/pygnulib/GLError.py
index 184d65f59c..4288820c97 100644
--- a/pygnulib/GLError.py
+++ b/pygnulib/GLError.py
@@ -19,6 +19,7 @@
# Define global imports
#===============================================================================
import os
+from typing import Any
#===============================================================================
@@ -27,7 +28,11 @@
class GLError(Exception):
'''Exception handler for pygnulib classes.'''
- def __init__(self, errno: int, errinfo: str | float | None = None) -> None:
+ errno: int
+ errinfo: Any
+ args: tuple[int, Any]
+
+ def __init__(self, errno: int, errinfo: Any = None) -> None:
'''Each error has following parameters:
errno: code of error; used to catch error type
1: file does not exist in GLFileSystem: <file>
diff --git a/pygnulib/GLFileSystem.py b/pygnulib/GLFileSystem.py
index 028ba3885e..f8b7f54ab3 100644
--- a/pygnulib/GLFileSystem.py
+++ b/pygnulib/GLFileSystem.py
@@ -46,6 +46,8 @@ class GLFileSystem:
Its main method lookup(file) is used to find file in these directories or
combine it using Linux 'patch' utility.'''
+ config: GLConfig
+
def __init__(self, config: GLConfig) -> None:
'''Create new GLFileSystem instance. The only argument is localpath,
which can be an empty list.'''
@@ -139,6 +141,13 @@ def shouldLink(self, original: str, lookedup: str) -> bool:
class GLFileAssistant:
'''GLFileAssistant is used to help with file processing.'''
+ original: str | None
+ rewritten: str | None
+ added: list[str]
+ config: GLConfig
+ transformers: dict[str, tuple[re.Pattern, str] | None]
+ filesystem: GLFileSystem
+
def __init__(self, config: GLConfig, transformers: dict[str, tuple[re.Pattern, str] | None] | None = None) -> None:
'''Create GLFileAssistant instance.
diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py
index 47c0e83555..63877342d2 100644
--- a/pygnulib/GLImport.py
+++ b/pygnulib/GLImport.py
@@ -60,6 +60,14 @@ class GLImport:
scripts. However, if user needs just to use power of gnulib-tool, this class
is a very good choice.'''
+ mode: int
+ config: GLConfig
+ cache: GLConfig
+ emitter: GLEmiter
+ modulesystem: GLModuleSystem
+ moduletable: GLModuleTable
+ makefiletable: GLMakefileTable
+
def __init__(self, config: GLConfig, mode: int) -> None:
'''Create GLImport instance.
The first variable, mode, must be one of the values of the MODES dict
diff --git a/pygnulib/GLMakefileTable.py b/pygnulib/GLMakefileTable.py
index 16e22f914f..efd276d20c 100644
--- a/pygnulib/GLMakefileTable.py
+++ b/pygnulib/GLMakefileTable.py
@@ -33,6 +33,9 @@ class GLMakefileTable:
An edit may be removed; this is done by removing its 'var' key but
keeping it in the list. Removed edits must be ignored.'''
+ config: GLConfig
+ table: list[dict[str, str | bool]]
+
def __init__(self, config: GLConfig) -> None:
'''Create GLMakefileTable instance.'''
if type(config) is not GLConfig:
@@ -41,7 +44,7 @@ def __init__(self, config: GLConfig) -> None:
self.config = config
self.table = []
- def __getitem__(self, y: int) -> dict[str, bool]:
+ def __getitem__(self, y: int) -> dict[str, str | bool]:
'''x.__getitem__(y) = x[y]'''
if type(y) is not int:
raise TypeError('indices must be integers, not %s'
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index ba1c57eb3f..a5afb5af6f 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -24,6 +24,7 @@
import hashlib
import subprocess as sp
from collections import defaultdict
+from typing import Any, ClassVar
from .constants import (
DIRS,
ENCS,
@@ -46,6 +47,9 @@ class GLModuleSystem:
'''GLModuleSystem is used to operate with module system using dynamic
searching and patching.'''
+ config: GLConfig
+ filesystem: GLFileSystem
+
def __init__(self, config: GLConfig) -> None:
'''Create new GLModuleSystem instance. Some functions use GLFileSystem class
to look up a file in localpath or gnulib directories, or combine it through
@@ -155,14 +159,25 @@ class GLModule:
path. GLModule can get all information about module, get its dependencies,
files, etc.'''
- section_label_pattern = \
+ # Regular expression matching the start of a section in the module description.
+ section_label_pattern: ClassVar[re.Pattern] = \
re.compile(r'^(Description|Comment|Status|Notice|Applicability|'
+ r'Files|Depends-on|configure\.ac-early|configure\.ac|'
+ r'Makefile\.am|Include|Link|License|Maintainer):$',
re.M)
# List of characters allowed in shell identifiers.
- shell_id_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
+ shell_id_chars: ClassVar[str] = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
+
+ cache: dict[str, Any]
+ content: str
+ name: str
+ path: str
+ patched: bool
+ config: GLConfig
+ filesystem: GLFileSystem
+ modulesystem: GLModuleSystem
+ sections: dict[str, str]
def __init__(self, config: GLConfig, name: str, path: str, patched: bool = False) -> None:
'''Create new GLModule instance. Arguments are:
@@ -683,6 +698,20 @@ def getMaintainer(self) -> str:
class GLModuleTable:
'''GLModuleTable is used to work with the list of the modules.'''
+ dependers: defaultdict[GLModule, set[GLModule]]
+ conditionals: dict[tuple[GLModule, GLModule], str | bool]
+ unconditionals: set[GLModule]
+ base_modules: list[GLModule]
+ main_modules: list[GLModule]
+ tests_modules: list[GLModule]
+ final_modules: list[GLModule]
+ config: GLConfig
+ filesystem: GLFileSystem
+ modulesystem: GLModuleSystem
+ inc_all_direct_tests: bool
+ inc_all_indirect_tests: bool
+ avoids: set[GLModule]
+
def __init__(self, config: GLConfig, inc_all_direct_tests: bool, inc_all_indirect_tests: bool) -> None:
'''Create new GLModuleTable instance. If modules are specified, then add
every module from iterable as unconditional module. If avoids is specified,
diff --git a/pygnulib/GLTestDir.py b/pygnulib/GLTestDir.py
index 002eb30267..fef92fb37b 100644
--- a/pygnulib/GLTestDir.py
+++ b/pygnulib/GLTestDir.py
@@ -91,6 +91,14 @@ class GLTestDir:
'''GLTestDir class is used to create a scratch package with the given
list of the modules.'''
+ config: GLConfig
+ testdir: str
+ emitter: GLEmiter
+ filesystem: GLFileSystem
+ modulesystem: GLModuleSystem
+ assistant: GLFileAssistant
+ makefiletable: GLMakefileTable
+
def __init__(self, config: GLConfig, testdir: str) -> None:
'''Create new GLTestDir instance.'''
if type(config) is not GLConfig:
@@ -846,6 +854,10 @@ class GLMegaTestDir:
'''GLMegaTestDir class is used to create a mega scratch package with the
given modules one by one and all together.'''
+ config: GLConfig
+ megatestdir: str
+ modulesystem: GLModuleSystem
+
def __init__(self, config: GLConfig, megatestdir: str) -> None:
'''Create new GLTestDir instance.'''
if type(config) is not GLConfig:
--
2.44.0