[neon-notifications] Changes in repo-metadata

Neon CI noreply at kde.org
Sat Aug 12 19:46:20 BST 2023


commit 6a4ff647e3ae18a8c9eea5baa563fdc0d68d6c13
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 15:23:09 2023 +0200

    Fix pylint errors: missing-function-docstring

diff --git a/.pylintrc b/.pylintrc
index 13438ca5..83ba2600 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -9,6 +9,5 @@ disable=
     invalid-name,
     line-too-long,
     missing-class-docstring,
-    missing-function-docstring,
     missing-module-docstring,
     too-many-locals,
diff --git a/dependencies/tools/list_dependencies b/dependencies/tools/list_dependencies
index 9fd82952..6232df7c 100755
--- a/dependencies/tools/list_dependencies
+++ b/dependencies/tools/list_dependencies
@@ -97,20 +97,21 @@ class KDEDependencies:
         self.implicitDependencies: Dict[str, str] = self.dependencies.get('*', {}).get('*', {})
         self.showDirectOnly = False
 
-    # Whether or not to show all dependencies recursively, or only direct
-    # dependencies. Note: I wasn't able to implement correct ordering if
-    # direct dependencies only are shown. It turns out to be quite nuanced too
-    # so be careful if you fix this yourself, as getting the ordering right
-    # still requires going recursively through every possible dependency.
     def setShowDirectOnly(self, showDirectOnly: bool) -> None:
+        """Whether or not to show all dependencies recursively, or only direct
+        dependencies. Note: I wasn't able to implement correct ordering if
+        direct dependencies only are shown. It turns out to be quite nuanced too
+        so be careful if you fix this yourself, as getting the ordering right
+        still requires going recursively through every possible dependency.
+        """
         self.showDirectOnly = showDirectOnly
 
-    # Read dependencies in from the given file.
     def importDependencies(self, fileName: str) -> Tuple[
         Dict[str, Dict[str, Dict[str, str]]],
         Dict[str, Dict[str, Dict[str, str]]],
         Set[str],
     ]:
+        """Read dependencies in from the given file."""
         dependencies: Dict[str, Dict[str, Dict[str, str]]] = {}
         negativeDeps: Dict[str, Dict[str, Dict[str, str]]] = {}
         wildcardItems: Set[str] = set()
@@ -152,8 +153,8 @@ class KDEDependencies:
 
         return dependencies, negativeDeps, wildcardItems
 
-    # Splits a foo[foo-branch] dependency into its item and branch pairs
     def itemToPathAndBranch(self, item: str) -> Tuple[str, str]:
+        """Splits a foo[foo-branch] dependency into its item and branch pairs"""
 
         # Look for match everything up to [, then [,
         # then match to end of line and find the last ]
@@ -162,8 +163,8 @@ class KDEDependencies:
             return result.group(1), result.group(2)
         return item, '*' # No branch, use wildcard
 
-    # Merges module and branch into a single item for storage
     def keyFromModuleBranch(self, module: str, branch: str) -> Tuple[str, str]:
+        """Merges module and branch into a single item for storage"""
         return (module, branch)
 
     #
@@ -245,9 +246,10 @@ class KDEDependencies:
 
         return node
 
-    # Takes the "node" as returned from _findDepsInternal and pretty prints
-    # a tree version of the dependencies, without removing common deps.
     def printTree(self, node: Node, level: int=0) -> None:
+        """Takes the "node" as returned from _findDepsInternal and pretty prints
+        a tree version of the dependencies, without removing common deps.
+        """
         branch = node.branch
         spacing = ' '.ljust(level)
         if branch != '*':
@@ -258,18 +260,20 @@ class KDEDependencies:
         for child in node.children:
             self.printTree(child, level + 2)
 
-    # Prints a module/branch combination, showing the branch only if it was
-    # actually set or otherwise mentioned (most dependencies are
-    # branch-independent).
     def printableModuleBranch(self, module: str, branch: str) -> str:
+        """Prints a module/branch combination, showing the branch only if it was
+        actually set or otherwise mentioned (most dependencies are
+        branch-independent).
+        """
         if branch != '*':
             return f"{module}[{branch}]"
         return module
 
-    # Takes a "node" returned by _findDepsInternal and prints all of the
-    # dependencies in pre-order fashion. Dependency items are only printed
-    # once, the first time they are encountered.
     def printOrdering(self, node: Node, visitedSet: Set[str]) -> None:
+        """Takes a "node" returned by _findDepsInternal and prints all of the
+        dependencies in pre-order fashion. Dependency items are only printed
+        once, the first time they are encountered.
+        """
         module = node.node
         branch = node.branch
 
@@ -279,9 +283,8 @@ class KDEDependencies:
                 self.printOrdering(child, visitedSet)
             print(self.printableModuleBranch(module, branch))
 
-    # Finds dependencies of the given modules (plural) and prints them
-    # out.
     def findDependenciesOf(self, modules: List[str], branch: str) -> None:
+        """Finds dependencies of the given modules (plural) and prints them."""
         if self.showDirectOnly:
             # TODO: Fix to keep right order. Set setShowDirectOnly's comment
             for module in modules:
@@ -300,21 +303,29 @@ class KDEDependencies:
             for child in node.children:
                 self.printOrdering(child, visitSet)
 
-    def contains(self, what: str) -> bool:
-        return what in self.dependencies
+    def contains(self, module: str) -> bool:
+        """Does the module occur in the dependencies?"""
+        return module in self.dependencies
 
     def allModules(self) -> KeysView[str]:
+        """Return an iterator over all module names."""
         return self.dependencies.keys()
 
 def addPathIfMissing(
     deps: KDEDependencies, modules: List[str], ignore_missing: bool = False
 ) -> Tuple[List[str], List[str]]:
+    """Partition a list of modules into good and bad modules
+
+    The good modules will be expanded to the full path (if they are not already
+    the full path) by finding the first module in the list of dependencies where
+    the name matches that of the dependency.
+    """
     good = []
     bad = []
     for m in modules:
         if deps.contains(m):
             good.append(m)
-        elif m.find("/") == -1:
+        elif "/" not in m:
             found = False
             for mod in deps.allModules():
                 if mod.endswith("/"+m):
@@ -333,6 +344,7 @@ def addPathIfMissing(
     return (good, bad)
 
 def main() -> None:
+    """The main function of this script"""
     arg_parser = argparse.ArgumentParser(
             description="Shows the git.kde.org dependencies of git.kde.org modules.")
     arg_parser.add_argument("-d", "--direct-dependencies", action="store_true",
diff --git a/dependencies/tools/list_preferred_repo_branch b/dependencies/tools/list_preferred_repo_branch
index 92a64ca5..a0d886b9 100755
--- a/dependencies/tools/list_preferred_repo_branch
+++ b/dependencies/tools/list_preferred_repo_branch
@@ -65,6 +65,7 @@ class LogicalGroups:
         self.catch_alls.sort(key=lambda x:x[1], reverse=True)
 
     def importLogicalGroups(self, fileName: str) -> LogicalModuleStructure:
+        """Load the logical module structure from a JSON file"""
         with open(fileName, "r", encoding="utf-8") as file:
             lms_json = json.load(file)
             result = LogicalModuleStructure(**lms_json)
@@ -78,6 +79,7 @@ class LogicalGroups:
         return self.logical_groups[module_spec][group]
 
     def findModuleBranch(self, module: str, group: str) -> Optional[str]:
+        """Find the branch for the given module"""
         if module in self.logical_groups:
             return self._findLogicalGroup(module, group)
         # Find all 'catch-all' definitions that aren't actually too long to
@@ -102,6 +104,7 @@ class LogicalGroups:
         return None
 
 def main() -> None:
+    """The main function of this script"""
     arg_parser = argparse.ArgumentParser(
             description="Maps git.kde.org modules and logical groups to git branches. " +
             "If no branch is configured for a given module path, a string containing <nothing set> " +

commit da898217daf0b3d8259b8f8d414f988b9f694be6
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 13:39:37 2023 +0200

    Fix pylint errors: redefined-outer-name

diff --git a/.pylintrc b/.pylintrc
index 161ad452..13438ca5 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -11,5 +11,4 @@ disable=
     missing-class-docstring,
     missing-function-docstring,
     missing-module-docstring,
-    redefined-outer-name,
     too-many-locals,
diff --git a/dependencies/tools/list_dependencies b/dependencies/tools/list_dependencies
index 26e06146..9fd82952 100755
--- a/dependencies/tools/list_dependencies
+++ b/dependencies/tools/list_dependencies
@@ -332,7 +332,7 @@ def addPathIfMissing(
 
     return (good, bad)
 
-if __name__ == '__main__':
+def main() -> None:
     arg_parser = argparse.ArgumentParser(
             description="Shows the git.kde.org dependencies of git.kde.org modules.")
     arg_parser.add_argument("-d", "--direct-dependencies", action="store_true",
@@ -371,3 +371,6 @@ if __name__ == '__main__':
         sys.exit(1)
     else:
         deps.findDependenciesOf(modules, args.branch)
+
+if __name__ == '__main__':
+    main()
diff --git a/dependencies/tools/list_preferred_repo_branch b/dependencies/tools/list_preferred_repo_branch
index da5c1dcd..92a64ca5 100755
--- a/dependencies/tools/list_preferred_repo_branch
+++ b/dependencies/tools/list_preferred_repo_branch
@@ -101,7 +101,7 @@ class LogicalGroups:
 
         return None
 
-if __name__ == '__main__':
+def main() -> None:
     arg_parser = argparse.ArgumentParser(
             description="Maps git.kde.org modules and logical groups to git branches. " +
             "If no branch is configured for a given module path, a string containing <nothing set> " +
@@ -139,3 +139,6 @@ if __name__ == '__main__':
         except KeyError as k:
             sys.stderr.write (f"An error occurred: {k}\n")
             sys.exit(1)
+
+if __name__ == '__main__':
+    main()

commit a7055bbd2c559b15bafe37b493ad45b606fa282f
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 13:36:51 2023 +0200

    Fix more pylint errors

diff --git a/.pylintrc b/.pylintrc
index d7d0d255..161ad452 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -4,12 +4,8 @@ extension-pkg-whitelist=pydantic
 
 [MESSAGES CONTROL]
 disable=
-    consider-using-f-string,
-    consider-using-sys-exit,
-    consider-using-with,
     duplicate-code,
     fixme,
-    format-string-without-interpolation,
     invalid-name,
     line-too-long,
     missing-class-docstring,
diff --git a/dependencies/tools/build_order b/dependencies/tools/build_order
index 239c219d..38b7c8c4 100755
--- a/dependencies/tools/build_order
+++ b/dependencies/tools/build_order
@@ -36,7 +36,7 @@ G = nx.DiGraph()
 
 if len(sys.argv) < 2:
     print("Missing dependency file to parse")
-    exit(-1)
+    sys.exit(-1)
 
 with open(sys.argv[1], encoding="utf-8") as f:
     lines = f.readlines()
diff --git a/dependencies/tools/list_dependencies b/dependencies/tools/list_dependencies
index cd32ab40..26e06146 100755
--- a/dependencies/tools/list_dependencies
+++ b/dependencies/tools/list_dependencies
@@ -145,8 +145,7 @@ class KDEDependencies:
                     # Verify same branch
                     curBranchDep = dictBranchItem[dependency]
                     if curBranchDep != dependencyBranch:
-                        msg = '%s:%s depends on %s and two of its branches, %s and %s' % (\
-                                repo, repoBranch, dependency, dependencyBranch, curBranchDep)
+                        msg = f'{repo}:{repoBranch} depends on {dependency} and two of its branches, {dependencyBranch} and {curBranchDep}'
                         raise RuntimeError(msg)
 
                 dictBranchItem[dependency] = dependencyBranch
@@ -242,8 +241,7 @@ class KDEDependencies:
             self.memoizedDependencies[module] = node
         else:
             if node.branch != branch and branch != "*" and node.branch != "*":
-                raise RuntimeError("%s depends on branch %s and on branch %s!" %
-                        (module, branch, node.branch))
+                raise RuntimeError(f"{module} depends on branch {branch} and on branch {node.branch}!")
 
         return node
 
@@ -251,10 +249,11 @@ class KDEDependencies:
     # a tree version of the dependencies, without removing common deps.
     def printTree(self, node: Node, level: int=0) -> None:
         branch = node.branch
+        spacing = ' '.ljust(level)
         if branch != '*':
-            print("%s%s[%s]" % (' '.ljust(level), node.node, node.branch))
+            print(f"{spacing}{node.node}[{branch}]")
         else:
-            print("%s%s" % (' '.ljust(level), node.node))
+            print(f"{spacing}{node.node}")
 
         for child in node.children:
             self.printTree(child, level + 2)
@@ -264,8 +263,8 @@ class KDEDependencies:
     # branch-independent).
     def printableModuleBranch(self, module: str, branch: str) -> str:
         if branch != '*':
-            return "%s[%s]" % (module, branch)
-        return "%s" % (module)
+            return f"{module}[{branch}]"
+        return module
 
     # Takes a "node" returned by _findDepsInternal and prints all of the
     # dependencies in pre-order fashion. Dependency items are only printed
@@ -286,7 +285,7 @@ class KDEDependencies:
         if self.showDirectOnly:
             # TODO: Fix to keep right order. Set setShowDirectOnly's comment
             for module in modules:
-                print("%s:" % (module))
+                print(f"{module}:")
                 node = self._findDepsInternal(module, branch)
                 for child in node.children:
                     module, branch = child.node, child.branch
@@ -361,14 +360,14 @@ if __name__ == '__main__':
             action='version', version='%(prog)s ' + str(version))
     args = arg_parser.parse_args()
 
-    deps = KDEDependencies("%s/dependency-data-%s" % (args.metadata_path, args.branch_group))
+    deps = KDEDependencies(f"{args.metadata_path}/dependency-data-{args.branch_group}")
     deps.setShowDirectOnly(args.direct_dependencies)
 
     (modules, mistake_modules) = addPathIfMissing(deps, args.module_path, args.assume_present)
     if len(mistake_modules) > 0:
         print("Error: Couldn't find the following modules:")
         for module in mistake_modules:
-            print("\t%s" % (module))
+            print(f"\t{module}")
         sys.exit(1)
     else:
         deps.findDependenciesOf(modules, args.branch)
diff --git a/dependencies/tools/list_preferred_repo_branch b/dependencies/tools/list_preferred_repo_branch
index c69ab2eb..da5c1dcd 100755
--- a/dependencies/tools/list_preferred_repo_branch
+++ b/dependencies/tools/list_preferred_repo_branch
@@ -133,9 +133,9 @@ if __name__ == '__main__':
                 branch = "<not in that branch-group>"
 
             if args.print_module_path:
-                print ("%s: %s" % (module_path, branch))
+                print (f"{module_path}: {branch}")
             else:
                 print (branch)
         except KeyError as k:
-            sys.stderr.write ("An error occurred: %s\n" % k)
+            sys.stderr.write (f"An error occurred: {k}\n")
             sys.exit(1)
diff --git a/git-helpers/git-kclone b/git-helpers/git-kclone
index 49811a49..3b51daa6 100755
--- a/git-helpers/git-kclone
+++ b/git-helpers/git-kclone
@@ -125,7 +125,7 @@ for arg in sys.argv[2:]:
 for repository in reposToClone:
     # Check to see if we already have something on disk....
     if os.path.exists( repository['identifier'] ):
-        print("WARNING: The repository '{0}' already exists - skipping".format( repository['identifier'] ))
+        print(f"WARNING: The repository '{repository['identifier']}' already exists - skipping")
         continue
 
     # Assemble our clone command
diff --git a/git-helpers/git-kpull b/git-helpers/git-kpull
index f2ab47bd..b72d35f5 100755
--- a/git-helpers/git-kpull
+++ b/git-helpers/git-kpull
@@ -33,8 +33,8 @@ for currentPath, subdirectories, filesInFolder in os.walk( repositoryMetadataPre
     # Now that we know we have something to work with....
     # Lets load the current metadata up
     metadataPath = os.path.join( currentPath, 'metadata.yaml' )
-    metadataFile = open( metadataPath, 'r', encoding="utf-8" )
-    metadata = yaml.safe_load( metadataFile )
+    with open( metadataPath, 'r', encoding="utf-8") as metadataFile:
+        metadata = yaml.safe_load( metadataFile )
 
     # Have we found a repository?
     # If it is a repository, is it active?
@@ -50,10 +50,10 @@ for currentPath, subdirectories, filesInFolder in os.walk( repositoryMetadataPre
 # Now that we know about everything, we can try to determine which repository we are in here...
 # Get the current url in use
 gitGetPushUrlCommand = 'git remote get-url --push origin'
-process = subprocess.Popen( gitGetPushUrlCommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=os.getcwd() )
-if process.stdout is None:
-    raise ValueError("process.stdout is undefined")
-currentPushUrl = process.stdout.readline().decode('utf-8').strip()
+with subprocess.Popen( gitGetPushUrlCommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=os.getcwd() ) as process:
+    if process.stdout is None:
+        raise ValueError("process.stdout is undefined")
+    currentPushUrl = process.stdout.readline().decode('utf-8').strip()
 currentPushUrl = re.sub(r'\.git$', '', currentPushUrl) # remove any existing .git suffix, added anyway again
 
 # Pull the repository identifier out of that

commit b736c0f6edadda8e9c18946ead61195f831280e4
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 13:26:31 2023 +0200

    Fix more pylint errors

diff --git a/.pylintrc b/.pylintrc
index 175be7c9..d7d0d255 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -16,10 +16,4 @@ disable=
     missing-function-docstring,
     missing-module-docstring,
     redefined-outer-name,
-    superfluous-parens,
     too-many-locals,
-    trailing-whitespace,
-    unnecessary-semicolon,
-    unspecified-encoding,
-    unused-variable,
-    wrong-import-order,
diff --git a/dependencies/tools/build_order b/dependencies/tools/build_order
index 3f7a948b..239c219d 100755
--- a/dependencies/tools/build_order
+++ b/dependencies/tools/build_order
@@ -27,9 +27,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import networkx as nx
 import re
 import sys
+import networkx as nx
 #import matplotlib.pyplot as plt
 
 G = nx.DiGraph()
@@ -38,7 +38,7 @@ if len(sys.argv) < 2:
     print("Missing dependency file to parse")
     exit(-1)
 
-with open(sys.argv[1]) as f:
+with open(sys.argv[1], encoding="utf-8") as f:
     lines = f.readlines()
     greedy_rules = []
     negative_rules = []
@@ -65,13 +65,13 @@ with open(sys.argv[1]) as f:
 
             G.add_edge(item, dependency)
         else:
-            print("Could not parse", line);
+            print("Could not parse", line)
 
     for greedy_rule in greedy_rules:
         greedy_pattern = greedy_rule[0].replace("*", ".*")
         dependency = greedy_rule[1]
         if dependency.startswith("-"):
-            print("Script does not support greedy negative rules", greedy_rule);
+            print("Script does not support greedy negative rules", greedy_rule)
         for node in list(G.nodes()):
             if re.search(greedy_pattern, node):
                 if node != dependency:
diff --git a/dependencies/tools/list_dependencies b/dependencies/tools/list_dependencies
index d18017cf..cd32ab40 100755
--- a/dependencies/tools/list_dependencies
+++ b/dependencies/tools/list_dependencies
@@ -115,7 +115,7 @@ class KDEDependencies:
         negativeDeps: Dict[str, Dict[str, Dict[str, str]]] = {}
         wildcardItems: Set[str] = set()
 
-        with open(fileName, 'r') as file:
+        with open(fileName, 'r', encoding="utf-8") as file:
             for line in file:
                 line = line.partition('#')[0].lstrip()
                 if not line:
@@ -155,7 +155,6 @@ class KDEDependencies:
 
     # Splits a foo[foo-branch] dependency into its item and branch pairs
     def itemToPathAndBranch(self, item: str) -> Tuple[str, str]:
-        branch = "*"
 
         # Look for match everything up to [, then [,
         # then match to end of line and find the last ]
@@ -359,7 +358,7 @@ if __name__ == '__main__':
     arg_parser.add_argument("-f", "--assume-present", action='store_true',
             help="If set, assume all input modules are present, and list implicit dependencies")
     arg_parser.add_argument("-v", "--version",
-            action='version', version=('%(prog)s ' + str(version)))
+            action='version', version='%(prog)s ' + str(version))
     args = arg_parser.parse_args()
 
     deps = KDEDependencies("%s/dependency-data-%s" % (args.metadata_path, args.branch_group))
diff --git a/dependencies/tools/list_preferred_repo_branch b/dependencies/tools/list_preferred_repo_branch
index d5b9987b..c69ab2eb 100755
--- a/dependencies/tools/list_preferred_repo_branch
+++ b/dependencies/tools/list_preferred_repo_branch
@@ -11,13 +11,13 @@
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
 # are met:
-# 
+#
 # 1. Redistributions of source code must retain the above copyright
 #    notice, this list of conditions and the following disclaimer.
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 
+#
 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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.
@@ -115,7 +115,7 @@ if __name__ == '__main__':
             default="../logical-module-structure",
             help="Path to logical-module-structure file (default: %(default)s)")
     arg_parser.add_argument("-v", "--version",
-            action='version', version=('%(prog)s ' + str(version)))
+            action='version', version='%(prog)s ' + str(version))
     arg_parser.add_argument("-p", "--print-module-path", action="store_true",
             help="Prints the module path before the branch (useful when multiple modules passed)")
     args = arg_parser.parse_args()
diff --git a/git-helpers/git-kclone b/git-helpers/git-kclone
index de949205..49811a49 100755
--- a/git-helpers/git-kclone
+++ b/git-helpers/git-kclone
@@ -2,9 +2,9 @@
 import os
 import shutil
 import sys
-import yaml
 import fnmatch
 import subprocess
+import yaml
 
 # Settings
 gitClonePattern = "https://invent.kde.org/{0}.git"
@@ -36,8 +36,8 @@ for currentPath, subdirectories, filesInFolder in os.walk( repositoryMetadataPre
     # Now that we know we have something to work with....
     # Lets load the current metadata up
     metadataPath = os.path.join( currentPath, 'metadata.yaml' )
-    metadataFile = open( metadataPath, 'r' )
-    metadata = yaml.safe_load( metadataFile )
+    with open( metadataPath, 'r', encoding="utf-8" ) as metadataFile:
+        metadata = yaml.safe_load( metadataFile )
 
     # Have we found a repository?
     # If it is a repository, is it active?
diff --git a/git-helpers/git-kpull b/git-helpers/git-kpull
index 1fbd0ec9..f2ab47bd 100755
--- a/git-helpers/git-kpull
+++ b/git-helpers/git-kpull
@@ -2,8 +2,8 @@
 import os
 import re
 import sys
-import yaml
 import subprocess
+import yaml
 
 # Urls to update what we have currently to
 newPullPattern = "https://invent.kde.org/{0}.git"
@@ -33,7 +33,7 @@ for currentPath, subdirectories, filesInFolder in os.walk( repositoryMetadataPre
     # Now that we know we have something to work with....
     # Lets load the current metadata up
     metadataPath = os.path.join( currentPath, 'metadata.yaml' )
-    metadataFile = open( metadataPath, 'r' )
+    metadataFile = open( metadataPath, 'r', encoding="utf-8" )
     metadata = yaml.safe_load( metadataFile )
 
     # Have we found a repository?
diff --git a/verify-repo-metadata.py b/verify-repo-metadata.py
index 20a61252..3b8159ac 100755
--- a/verify-repo-metadata.py
+++ b/verify-repo-metadata.py
@@ -1,9 +1,9 @@
 #!/usr/bin/env python3
 import os
 import sys
-import yaml
 import argparse
 import regex
+import yaml
 
 # Gather the command line arguments we need
 parser = argparse.ArgumentParser(description='Verifies the metadata files')
@@ -33,8 +33,8 @@ for currentPath, subdirectories, filesInFolder in os.walk( args.metadata_path, t
     # Now that we know we have something to work with....
     # Lets load the current metadata up
     metadataPath = os.path.join( currentPath, 'metadata.yaml' )
-    metadataFile = open( metadataPath, 'r' )
-    metadata = yaml.safe_load( metadataFile )
+    with open( metadataPath, 'r', encoding="utf-8" ) as metadataFile:
+        metadata = yaml.safe_load( metadataFile )
 
     # Trim the base path to the metadata off to make it a bit nicer to read
     currentLocation = currentPath[len(args.metadata_path):]

commit f8e570f4811e234bca018c9189cbcb7d0494e6e3
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 13:16:08 2023 +0200

    Fix pylint errors: no-else-return, use-dict-literal, use-list-literal

diff --git a/.pylintrc b/.pylintrc
index 7e2b2dfa..175be7c9 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -4,7 +4,6 @@ extension-pkg-whitelist=pydantic
 
 [MESSAGES CONTROL]
 disable=
-    cell-var-from-loop,
     consider-using-f-string,
     consider-using-sys-exit,
     consider-using-with,
@@ -16,8 +15,6 @@ disable=
     missing-class-docstring,
     missing-function-docstring,
     missing-module-docstring,
-    multiple-imports,
-    no-else-return,
     redefined-outer-name,
     superfluous-parens,
     too-many-locals,
@@ -25,6 +22,4 @@ disable=
     unnecessary-semicolon,
     unspecified-encoding,
     unused-variable,
-    use-dict-literal,
-    use-list-literal,
     wrong-import-order,
diff --git a/dependencies/tools/list_dependencies b/dependencies/tools/list_dependencies
index 5890ce7f..d18017cf 100755
--- a/dependencies/tools/list_dependencies
+++ b/dependencies/tools/list_dependencies
@@ -90,8 +90,8 @@ class Node:
 # }
 class KDEDependencies:
     def __init__(self, fileName: str) -> None:
-        self.memoizedDependencies: Dict[str, Node] = dict()
-        self.wildcardDependencies: Dict[str, str] = dict()
+        self.memoizedDependencies: Dict[str, Node] = {}
+        self.wildcardDependencies: Dict[str, str] = {}
         self.dependencies, self.negativeDeps, self.wildcardItems \
                 = self.importDependencies(fileName)
         self.implicitDependencies: Dict[str, str] = self.dependencies.get('*', {}).get('*', {})
@@ -111,44 +111,45 @@ class KDEDependencies:
         Dict[str, Dict[str, Dict[str, str]]],
         Set[str],
     ]:
-        dependencies: Dict[str, Dict[str, Dict[str, str]]] = dict()
-        negativeDeps: Dict[str, Dict[str, Dict[str, str]]] = dict()
+        dependencies: Dict[str, Dict[str, Dict[str, str]]] = {}
+        negativeDeps: Dict[str, Dict[str, Dict[str, str]]] = {}
         wildcardItems: Set[str] = set()
 
         with open(fileName, 'r') as file:
             for line in file:
                 line = line.partition('#')[0].lstrip()
-                if line:
-                    lineParts = line.partition(':')
-                    repoItem = lineParts[0].lstrip().rstrip()
-                    dependentItem = lineParts[2].lstrip().rstrip()
-
-                    repo, repoBranch = self.itemToPathAndBranch(repoItem)
-                    if repo.endswith('/*'):
-                        wildcardItems.add(repo.rstrip('/*'))
-
-                    negativeDep = False
-                    if dependentItem.startswith('-'):
-                        negativeDep = True
-                        dependentItem = dependentItem.lstrip('-')
-                    dependency, dependencyBranch = self.itemToPathAndBranch(dependentItem)
-
-                    dictRepoItem = None
-                    if negativeDep:
-                        dictRepoItem = negativeDeps.setdefault(repo, dict())
-                    else:
-                        dictRepoItem = dependencies.setdefault(repo, dict())
-
-                    dictBranchItem = dictRepoItem.setdefault(repoBranch, dict())
-                    if dependency in dictBranchItem:
-                        # Verify same branch
-                        curBranchDep = dictBranchItem[dependency]
-                        if curBranchDep != dependencyBranch:
-                            msg = '%s:%s depends on %s and two of its branches, %s and %s' % (\
-                                    repo, repoBranch, dependency, dependencyBranch, curBranchDep)
-                            raise RuntimeError(msg)
-
-                    dictBranchItem[dependency] = dependencyBranch
+                if not line:
+                    continue
+                lineParts = line.partition(':')
+                repoItem = lineParts[0].lstrip().rstrip()
+                dependentItem = lineParts[2].lstrip().rstrip()
+
+                repo, repoBranch = self.itemToPathAndBranch(repoItem)
+                if repo.endswith('/*'):
+                    wildcardItems.add(repo.rstrip('/*'))
+
+                negativeDep = False
+                if dependentItem.startswith('-'):
+                    negativeDep = True
+                    dependentItem = dependentItem.lstrip('-')
+                dependency, dependencyBranch = self.itemToPathAndBranch(dependentItem)
+
+                dictRepoItem = None
+                if negativeDep:
+                    dictRepoItem = negativeDeps.setdefault(repo, {})
+                else:
+                    dictRepoItem = dependencies.setdefault(repo, {})
+
+                dictBranchItem = dictRepoItem.setdefault(repoBranch, {})
+                if dependency in dictBranchItem:
+                    # Verify same branch
+                    curBranchDep = dictBranchItem[dependency]
+                    if curBranchDep != dependencyBranch:
+                        msg = '%s:%s depends on %s and two of its branches, %s and %s' % (\
+                                repo, repoBranch, dependency, dependencyBranch, curBranchDep)
+                        raise RuntimeError(msg)
+
+                dictBranchItem[dependency] = dependencyBranch
 
         return dependencies, negativeDeps, wildcardItems
 
@@ -161,8 +162,7 @@ class KDEDependencies:
         result = re.search(r'(^[^\[]*)[\[](.*)]$', item)
         if result:
             return result.group(1), result.group(2)
-        else:
-            return item, '*' # No branch, use wildcard
+        return item, '*' # No branch, use wildcard
 
     # Merges module and branch into a single item for storage
     def keyFromModuleBranch(self, module: str, branch: str) -> Tuple[str, str]:
@@ -207,7 +207,7 @@ class KDEDependencies:
     # the result as a tree under node. To be useful the tree of node and its
     # children must still be processed to get the list of dependencies.
     def _addEffectiveDeps(self, node: Node, module: str, branch: str) -> None:
-        depCandidates: List[Tuple[str, str]] = list()
+        depCandidates: List[Tuple[str, str]] = []
         for w in self.wildcardItems:
             if not module.endswith('/*') and module.startswith(w + '/'):
                 wildcardRepo = w + "/*"
@@ -266,8 +266,7 @@ class KDEDependencies:
     def printableModuleBranch(self, module: str, branch: str) -> str:
         if branch != '*':
             return "%s[%s]" % (module, branch)
-        else:
-            return "%s" % (module)
+        return "%s" % (module)
 
     # Takes a "node" returned by _findDepsInternal and prints all of the
     # dependencies in pre-order fashion. Dependency items are only printed
diff --git a/dependencies/tools/list_preferred_repo_branch b/dependencies/tools/list_preferred_repo_branch
index b651a194..d5b9987b 100755
--- a/dependencies/tools/list_preferred_repo_branch
+++ b/dependencies/tools/list_preferred_repo_branch
@@ -80,27 +80,26 @@ class LogicalGroups:
     def findModuleBranch(self, module: str, group: str) -> Optional[str]:
         if module in self.logical_groups:
             return self._findLogicalGroup(module, group)
-        else:
-            # Find all 'catch-all' definitions that aren't actually too long to
-            # possibly match. We have to sort those out now as otherwise we'd
-            # have the possibility of an impossible match still having the
-            # longest common-prefix with our module path.
-            module_path_comps = module.count("/") + 1
-            search_gen = (group_spec[0] for group_spec in self.catch_alls
-                    if group_spec[2] < module_path_comps and
-                        (module.startswith(group_spec[1]) or
-                            group_spec[1] == "")) # This would always be from a
-                                                  # '*' entry, which would be last
-
-            # We use next to avoid searching the whole list when we just want
-            # the first match. Due to the way we sorted self.catch_alls the
-            # first match that makes it through the search_gen is the one we
-            # want.
-            winner = next(search_gen, None)
-            if winner:
-                return self._findLogicalGroup(winner, group)
-
-            return None
+        # Find all 'catch-all' definitions that aren't actually too long to
+        # possibly match. We have to sort those out now as otherwise we'd
+        # have the possibility of an impossible match still having the
+        # longest common-prefix with our module path.
+        module_path_comps = module.count("/") + 1
+        search_gen = (group_spec[0] for group_spec in self.catch_alls
+                if group_spec[2] < module_path_comps and
+                    (module.startswith(group_spec[1]) or
+                        group_spec[1] == "")) # This would always be from a
+                                              # '*' entry, which would be last
+
+        # We use next to avoid searching the whole list when we just want
+        # the first match. Due to the way we sorted self.catch_alls the
+        # first match that makes it through the search_gen is the one we
+        # want.
+        winner = next(search_gen, None)
+        if winner:
+            return self._findLogicalGroup(winner, group)
+
+        return None
 
 if __name__ == '__main__':
     arg_parser = argparse.ArgumentParser(

commit 09c31bb3e4d201dae13b6162750c4027960d1be9
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 13:14:29 2023 +0200

    Fix pylint errors: anomalous-backslash-in-string

diff --git a/.pylintrc b/.pylintrc
index cc41da9a..7e2b2dfa 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -4,7 +4,6 @@ extension-pkg-whitelist=pydantic
 
 [MESSAGES CONTROL]
 disable=
-    anomalous-backslash-in-string,
     cell-var-from-loop,
     consider-using-f-string,
     consider-using-sys-exit,
diff --git a/dependencies/tools/list_dependencies b/dependencies/tools/list_dependencies
index 19f7880c..5890ce7f 100755
--- a/dependencies/tools/list_dependencies
+++ b/dependencies/tools/list_dependencies
@@ -158,7 +158,7 @@ class KDEDependencies:
 
         # Look for match everything up to [, then [,
         # then match to end of line and find the last ]
-        result = re.search('(^[^\[]*)[\[](.*)]$', item)
+        result = re.search(r'(^[^\[]*)[\[](.*)]$', item)
         if result:
             return result.group(1), result.group(2)
         else:
diff --git a/verify-repo-metadata.py b/verify-repo-metadata.py
index 2108c9e2..20a61252 100755
--- a/verify-repo-metadata.py
+++ b/verify-repo-metadata.py
@@ -17,7 +17,7 @@ if not os.path.exists( args.metadata_path ):
 
 # Regular expression used to validate project names
 # Taken from Gitlab itself (with slight adaptation to be Python compatible)
-projectRegex = regex.compile('^[\p{Alnum}\u00A9-\u1f9ff_][\p{Alnum}\p{Pd}\u00A9-\u1f9ff_\. ]*$', flags=regex.V1)
+projectRegex = regex.compile(r'^[\p{Alnum}\u00A9-\u1f9ff_][\p{Alnum}\p{Pd}\u00A9-\u1f9ff_\. ]*$', flags=regex.V1)
 
 # Start a list of used project identifiers and names
 projectIdentifiersInUse = []

commit ea3bd0d5e32b2b55f9b60a9ac402eaefd36fbff4
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 13:10:06 2023 +0200

    Enable strict mode for mypy and add required type annotations
    
    pydantic is used to load the json file and check its structure.

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2138d2a0..2c5a7a7d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -32,8 +32,9 @@ lint:
     - pip3 install pylint mypy networkx types-PyYAML types-regex
     - FILES="*.py git-helpers/* dependencies/tools/*"
     - pylint $FILES
-    - mypy verify-repo-metadata.py
-    - mypy git-helpers/git-kclone
-    - mypy git-helpers/git-kpull
-    - mypy dependencies/tools/list_dependencies
-    - mypy dependencies/tools/list_preferred_repo_branch
+    - mypy --strict verify-repo-metadata.py
+    - mypy --strict git-helpers/git-kclone
+    - mypy --strict git-helpers/git-kpull
+    - mypy --strict --ignore-missing-imports dependencies/tools/build_order
+    - mypy --strict dependencies/tools/list_dependencies
+    - mypy --strict dependencies/tools/list_preferred_repo_branch
diff --git a/dependencies/tools/list_dependencies b/dependencies/tools/list_dependencies
index 6f6d9d29..19f7880c 100755
--- a/dependencies/tools/list_dependencies
+++ b/dependencies/tools/list_dependencies
@@ -28,10 +28,20 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import sys, argparse, re
+import argparse
+import re
+import sys
+from dataclasses import dataclass
+from typing import Dict, KeysView, List, Set, Tuple
 
 version = "0.1"
 
+ at dataclass
+class Node:
+    node: str
+    branch: str
+    children: List["Node"]
+
 # Handles all aspects of dependency parsing and ordering.
 # This is probably too monolithic though -- feel free to fix!
 #
@@ -79,12 +89,12 @@ version = "0.1"
 #   }
 # }
 class KDEDependencies:
-    def __init__(self, fileName):
-        self.memoizedDependencies = dict()
-        self.wildcardDependencies = dict()
+    def __init__(self, fileName: str) -> None:
+        self.memoizedDependencies: Dict[str, Node] = dict()
+        self.wildcardDependencies: Dict[str, str] = dict()
         self.dependencies, self.negativeDeps, self.wildcardItems \
                 = self.importDependencies(fileName)
-        self.implicitDependencies = self.dependencies.get('*', {}).get('*', [])
+        self.implicitDependencies: Dict[str, str] = self.dependencies.get('*', {}).get('*', {})
         self.showDirectOnly = False
 
     # Whether or not to show all dependencies recursively, or only direct
@@ -92,14 +102,18 @@ class KDEDependencies:
     # direct dependencies only are shown. It turns out to be quite nuanced too
     # so be careful if you fix this yourself, as getting the ordering right
     # still requires going recursively through every possible dependency.
-    def setShowDirectOnly(self, showDirectOnly):
+    def setShowDirectOnly(self, showDirectOnly: bool) -> None:
         self.showDirectOnly = showDirectOnly
 
     # Read dependencies in from the given file.
-    def importDependencies(self, fileName):
-        dependencies = dict()
-        negativeDeps = dict()
-        wildcardItems = set()
+    def importDependencies(self, fileName: str) -> Tuple[
+        Dict[str, Dict[str, Dict[str, str]]],
+        Dict[str, Dict[str, Dict[str, str]]],
+        Set[str],
+    ]:
+        dependencies: Dict[str, Dict[str, Dict[str, str]]] = dict()
+        negativeDeps: Dict[str, Dict[str, Dict[str, str]]] = dict()
+        wildcardItems: Set[str] = set()
 
         with open(fileName, 'r') as file:
             for line in file:
@@ -139,7 +153,7 @@ class KDEDependencies:
         return dependencies, negativeDeps, wildcardItems
 
     # Splits a foo[foo-branch] dependency into its item and branch pairs
-    def itemToPathAndBranch(self, item):
+    def itemToPathAndBranch(self, item: str) -> Tuple[str, str]:
         branch = "*"
 
         # Look for match everything up to [, then [,
@@ -151,7 +165,7 @@ class KDEDependencies:
             return item, '*' # No branch, use wildcard
 
     # Merges module and branch into a single item for storage
-    def keyFromModuleBranch(self, module, branch):
+    def keyFromModuleBranch(self, module: str, branch: str) -> Tuple[str, str]:
         return (module, branch)
 
     #
@@ -164,7 +178,7 @@ class KDEDependencies:
     # present in the list).
     #
 
-    def _addModuleBranchDirectDependencies(self, depCandidates, module, branch):
+    def _addModuleBranchDirectDependencies(self, depCandidates: List[Tuple[str, str]], module: str, branch: str) -> None:
         if module not in self.dependencies or branch not in self.dependencies[module]:
             return
         for depModule, depBranch in self.dependencies[module][branch].items():
@@ -174,7 +188,9 @@ class KDEDependencies:
             if newKey not in depCandidates:
                 depCandidates.append(newKey)
 
-    def _removeModuleBranchNegativeDependencies(self, depCandidates, module, branch):
+    def _removeModuleBranchNegativeDependencies(
+        self, depCandidates: List[Tuple[str, str]], module: str, branch: str
+    ) -> None:
         if module not in self.negativeDeps or branch not in self.negativeDeps[module]:
             return
         for depModule, depBranch in self.negativeDeps[module][branch].items():
@@ -182,16 +198,16 @@ class KDEDependencies:
                 # The [:] is just to ensure we're assigning to the list passed
                 # in to make depCandidates a mutable parameter, otherwise it
                 # would only be a local effect.
-                depCandidates[:] = filter(lambda x: not x.startswith(depModule), depCandidates)
+                depCandidates[:] = [x for x in depCandidates if not x[0] == depModule]
             else:
                 key = self.keyFromModuleBranch(depModule, depBranch)
-                depCandidates[:] = filter(lambda x: x != key, depCandidates)
+                depCandidates[:] = [x for x in depCandidates if x != key]
 
     # Adds all effective dependencies of the given module/branch, storing
     # the result as a tree under node. To be useful the tree of node and its
     # children must still be processed to get the list of dependencies.
-    def _addEffectiveDeps(self, node, module, branch):
-        depCandidates = list()
+    def _addEffectiveDeps(self, node: Node, module: str, branch: str) -> None:
+        depCandidates: List[Tuple[str, str]] = list()
         for w in self.wildcardItems:
             if not module.endswith('/*') and module.startswith(w + '/'):
                 wildcardRepo = w + "/*"
@@ -208,46 +224,46 @@ class KDEDependencies:
 
         # Don't let modules depend on themselves by accident
         key = self.keyFromModuleBranch(module, branch)
-        depCandidates = filter(lambda x: x != key, depCandidates)
+        depCandidates = list(filter(lambda x: x != key, depCandidates))
 
         for candidate in depCandidates:
             depModule, depBranch = candidate
             newNode = self._findDepsInternal(depModule, depBranch)
-            node["children"].append(newNode)
+            node.children.append(newNode)
 
     # Finds all dependencies recursively for the given module and branch,
     # returns a "node" structure (which is itself a tree) describing the
     # dependencies and their proper order.
-    def _findDepsInternal(self, module, branch):
+    def _findDepsInternal(self, module: str, branch: str) -> Node:
         node = self.memoizedDependencies.get(module, None)
         if not node:
-            node = { "node": module, "branch": branch, "children": [ ] }
+            node = Node(module, branch, [])
             if module not in self.implicitDependencies:
                 self._addEffectiveDeps(node, module, branch)
             self.memoizedDependencies[module] = node
         else:
-            if node["branch"] != branch and branch != "*" and node["branch"] != "*":
+            if node.branch != branch and branch != "*" and node.branch != "*":
                 raise RuntimeError("%s depends on branch %s and on branch %s!" %
-                        (module, branch, node["branch"]))
+                        (module, branch, node.branch))
 
         return node
 
     # Takes the "node" as returned from _findDepsInternal and pretty prints
     # a tree version of the dependencies, without removing common deps.
-    def printTree(self, node, level=0):
-        branch = node["branch"]
+    def printTree(self, node: Node, level: int=0) -> None:
+        branch = node.branch
         if branch != '*':
-            print("%s%s[%s]" % (' '.ljust(level), node["node"], node["branch"]))
+            print("%s%s[%s]" % (' '.ljust(level), node.node, node.branch))
         else:
-            print("%s%s" % (' '.ljust(level), node["node"]))
+            print("%s%s" % (' '.ljust(level), node.node))
 
-        for child in node["children"]:
+        for child in node.children:
             self.printTree(child, level + 2)
 
     # Prints a module/branch combination, showing the branch only if it was
     # actually set or otherwise mentioned (most dependencies are
     # branch-independent).
-    def printableModuleBranch(self, module, branch):
+    def printableModuleBranch(self, module: str, branch: str) -> str:
         if branch != '*':
             return "%s[%s]" % (module, branch)
         else:
@@ -256,26 +272,26 @@ class KDEDependencies:
     # Takes a "node" returned by _findDepsInternal and prints all of the
     # dependencies in pre-order fashion. Dependency items are only printed
     # once, the first time they are encountered.
-    def printOrdering(self, node, visitedSet):
-        module = node["node"]
-        branch = node["branch"]
+    def printOrdering(self, node: Node, visitedSet: Set[str]) -> None:
+        module = node.node
+        branch = node.branch
 
         if module not in visitedSet:
             visitedSet.add(module)
-            for child in node["children"]:
+            for child in node.children:
                 self.printOrdering(child, visitedSet)
             print(self.printableModuleBranch(module, branch))
 
     # Finds dependencies of the given modules (plural) and prints them
     # out.
-    def findDependenciesOf(self, modules, branch):
+    def findDependenciesOf(self, modules: List[str], branch: str) -> None:
         if self.showDirectOnly:
             # TODO: Fix to keep right order. Set setShowDirectOnly's comment
             for module in modules:
                 print("%s:" % (module))
                 node = self._findDepsInternal(module, branch)
-                for child in node["children"]:
-                    module, branch = child["node"], child["branch"]
+                for child in node.children:
+                    module, branch = child.node, child.branch
                     print("\t", self.printableModuleBranch(module, branch))
         else:
             # Fake supporting multiple module paths by merging into virtual dependent
@@ -283,17 +299,19 @@ class KDEDependencies:
             self.dependencies[rootModule] = { "*": { x : branch for x in modules } }
 
             node = self._findDepsInternal(rootModule, branch)
-            visitSet = set()
-            for child in node["children"]:
+            visitSet: Set[str] = set()
+            for child in node.children:
                 self.printOrdering(child, visitSet)
 
-    def contains(self, what):
+    def contains(self, what: str) -> bool:
         return what in self.dependencies
 
-    def allModules(self):
+    def allModules(self) -> KeysView[str]:
         return self.dependencies.keys()
 
-def addPathIfMissing(deps, modules, ignore_missing=False):
+def addPathIfMissing(
+    deps: KDEDependencies, modules: List[str], ignore_missing: bool = False
+) -> Tuple[List[str], List[str]]:
     good = []
     bad = []
     for m in modules:
diff --git a/dependencies/tools/list_preferred_repo_branch b/dependencies/tools/list_preferred_repo_branch
index 10245857..b651a194 100755
--- a/dependencies/tools/list_preferred_repo_branch
+++ b/dependencies/tools/list_preferred_repo_branch
@@ -29,14 +29,31 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import json, sys, argparse
+import argparse
+import json
+import sys
+from typing import Dict, Optional, Set
+
+from pydantic import BaseModel
 
 version = "0.2"
 
+
+class LogicalModuleStructure(BaseModel):
+    """The logical module structure.
+    It is documented at
+      http://community.kde.org/Infrastructure/Project_Metadata#kde-build-metadata
+    """
+
+    version: int
+    layers: Set[str] = set()
+    groups: Dict[str, Dict[str, str]] = {}
+
+
 class LogicalGroups:
-    def __init__(self, fileName):
+    def __init__(self, fileName: str) -> None:
         json_groups = self.importLogicalGroups(fileName)
-        self.logical_groups = json_groups['groups']
+        self.logical_groups = json_groups.groups
 
         # Speed-up searching of wildcarded groups by keeping them in their
         # own set for searching later. Keep the required prefix portion separate,
@@ -47,19 +64,20 @@ class LogicalGroups:
                 if group.endswith("*")]
         self.catch_alls.sort(key=lambda x:x[1], reverse=True)
 
-    def importLogicalGroups(self, fileName):
-        with open(fileName, 'r') as file:
-            result = json.load(file)
+    def importLogicalGroups(self, fileName: str) -> LogicalModuleStructure:
+        with open(fileName, "r", encoding="utf-8") as file:
+            lms_json = json.load(file)
+            result = LogicalModuleStructure(**lms_json)
         return result
 
-    def _findLogicalGroup(self, module_spec, group):
+    def _findLogicalGroup(self, module_spec: str, group: str) -> Optional[str]:
         if group not in self.logical_groups[module_spec]:
             # The spec allows for groups to be unspecified (esp. for wildcards).
             # Might be prudent to warn for this though...
             return None
         return self.logical_groups[module_spec][group]
 
-    def findModuleBranch(self, module, group):
+    def findModuleBranch(self, module: str, group: str) -> Optional[str]:
         if module in self.logical_groups:
             return self._findLogicalGroup(module, group)
         else:

commit 58b67bd9082be0df0bb221806bb4f03f9e8a6258
Author: Jos van den Oever <jos at vandenoever.info>
Date:   Sat Jul 29 12:40:42 2023 +0200

    Add CI job for linting python scripts with mypy and pylint
    
    So far all errors are disabled. This only adds the CI commands to run
    the tests.
    
    A few small code changes to make mypy check pass in non-strict mode were
    added as well.

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9a760ff2..2138d2a0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,3 +25,15 @@ check:
   stage: test
   script:
     - python3 verify-repo-metadata.py --metadata-path projects-invent
+
+lint:
+  stage: test
+  script:
+    - pip3 install pylint mypy networkx types-PyYAML types-regex
+    - FILES="*.py git-helpers/* dependencies/tools/*"
+    - pylint $FILES
+    - mypy verify-repo-metadata.py
+    - mypy git-helpers/git-kclone
+    - mypy git-helpers/git-kpull
+    - mypy dependencies/tools/list_dependencies
+    - mypy dependencies/tools/list_preferred_repo_branch
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 00000000..cc41da9a
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,31 @@
+[MAIN]
+
+extension-pkg-whitelist=pydantic
+
+[MESSAGES CONTROL]
+disable=
+    anomalous-backslash-in-string,
+    cell-var-from-loop,
+    consider-using-f-string,
+    consider-using-sys-exit,
+    consider-using-with,
+    duplicate-code,
+    fixme,
+    format-string-without-interpolation,
+    invalid-name,
+    line-too-long,
+    missing-class-docstring,
+    missing-function-docstring,
+    missing-module-docstring,
+    multiple-imports,
+    no-else-return,
+    redefined-outer-name,
+    superfluous-parens,
+    too-many-locals,
+    trailing-whitespace,
+    unnecessary-semicolon,
+    unspecified-encoding,
+    unused-variable,
+    use-dict-literal,
+    use-list-literal,
+    wrong-import-order,
diff --git a/git-helpers/git-kpull b/git-helpers/git-kpull
index cc33c153..1fbd0ec9 100755
--- a/git-helpers/git-kpull
+++ b/git-helpers/git-kpull
@@ -51,6 +51,8 @@ for currentPath, subdirectories, filesInFolder in os.walk( repositoryMetadataPre
 # Get the current url in use
 gitGetPushUrlCommand = 'git remote get-url --push origin'
 process = subprocess.Popen( gitGetPushUrlCommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=os.getcwd() )
+if process.stdout is None:
+    raise ValueError("process.stdout is undefined")
 currentPushUrl = process.stdout.readline().decode('utf-8').strip()
 currentPushUrl = re.sub(r'\.git$', '', currentPushUrl) # remove any existing .git suffix, added anyway again
 
diff --git a/requirements.txt b/requirements.txt
index b7c0fe20..d7b66b1c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
+pydantic
 pyyaml
 regex
diff --git a/verify-repo-metadata.py b/verify-repo-metadata.py
index f2f7b09c..2108c9e2 100755
--- a/verify-repo-metadata.py
+++ b/verify-repo-metadata.py
@@ -12,7 +12,7 @@ args = parser.parse_args()
 
 # Make sure our configuration file exists
 if not os.path.exists( args.metadata_path ):
-    print("Unable to locate specified metadata location: %s".format(args.metadata_path))
+    print(f"Unable to locate specified metadata location: {args.metadata_path}")
     sys.exit(1)
 
 # Regular expression used to validate project names



More information about the neon-notifications mailing list