#290 Make PkgHistoryProcessor less daunting (and misc)
Merged a year ago by nphilipp. Opened a year ago by nphilipp.
fedora-infra/ nphilipp/rpmautospec main--make-processor-less-daunting  into  main

file modified
-12
@@ -27,18 +27,6 @@ 

                  - python3-pytest-cov

                  - python3-pyyaml

                  - python3-rpm

-         - fi-pytest-f35:

-             vars:

-               dependencies:

-                 - glibc-langpack-de

-                 - glibc-langpack-en

-                 - python3-babel

-                 - python3-koji

-                 - python3-pygit2

-                 - python3-pytest

-                 - python3-pytest-cov

-                 - python3-pyyaml

-                 - python3-rpm

          - rpm-mock-build:

              vars:

                mock_config: fedora-36-x86_64

file modified
+52 -30
@@ -452,20 +452,23 @@ 

          self, head: pygit2.Commit, *, visitors: Sequence = (), seed_info: Dict[str, Any] = None

      ) -> Dict[pygit2.Commit, Dict[str, Any]]:

          """Process historical commits with visitors and gather results."""

+         # This sets the “playing field” for the head commit, it subs for the partial result of a

+         # child commit which doesn’t exist.

          seed_info = {"child_must_continue": True, **(seed_info or {})}

-         # maps visited commits to their (in-flight) visitors and if they must

-         # continue

+ 

+         # These map visited commits to their (in-flight) visitor coroutines and tracks if they must

+         # continue and other auxiliary information.

          commit_coroutines = {}

          commit_coroutines_info = {}

  

-         # keep track of branches

+         # This keeps track of the heads of branches that need to be processed.

          branch_heads = [head]

-         branches = []

+         # This stores the discovered snippets, i.e. linear chains of commits.

+         snippets = []

  

-         ########################################################################################

          # Unfortunately, pygit2 only tells us what the parents of a commit are, not what other

          # commits a commit is parent to (its children). Fortunately, Repository.walk() is quick.

-         ########################################################################################

+         # This maps parent commits to their children.

          commit_children = defaultdict(list)

          for commit in self.repo.walk(head.id):

              for parent in commit.parents:
@@ -475,6 +478,11 @@ 

          # To process, first walk the tree from the head commit downward, following all branches.

          # Check visitors whether they need parent results to do their work, i.e. the history needs

          # to be processed further, or just traversed.

+         #

+         # Here, the “top halves” of visitors get merged information from their child commit(s) as

+         # well as from visitors that ran prior on the same commit. In practice: during runtime,

+         # `changelog_visitor()` gets information from `release_number_visitor()` for the same

+         # commit.

          ##########################################################################################

  

          log.debug("===========================================================")
@@ -484,13 +492,14 @@ 

          # While new branch heads are encountered...

          while branch_heads:

              commit = branch_heads.pop(0)

-             branch = []

-             branches.append(branch)

+             snippet = []

+             snippets.append(snippet)

  

              keep_processing = True

  

              while True:

                  if commit in commit_coroutines:

+                     # This commit was processed already, so `snippet` can be cut off before it.

                      log.debug("%s: coroutines exist, skipping", commit.short_id)

                      break

  
@@ -498,16 +507,20 @@ 

                      log.debug("commit %s: %s", commit.short_id, commit.message.split("\n", 1)[0])

  

                  if commit == head:

+                     # Set the stage for the first commit: Visitors expect to get some information

+                     # from their child commit(s), as there aren’t any yet, fake it.

                      children_visitors_info = [seed_info for v in visitors]

                  else:

                      this_children = commit_children[commit]

                      if not all(child in commit_coroutines for child in this_children):

-                         # there's another branch that leads to this parent, put the remainder on the

-                         # stack

+                         # There's another branch that leads to this parent, put the remainder on the

+                         # stack.

                          branch_heads.append(commit)

-                         if not branch:

-                             # don't keep empty branches on the stack

-                             branches.pop()

+ 

+                         if not snippet:

+                             # Don't keep empty snippets on the stack.

+                             snippets.pop()

+ 

                          if log.isEnabledFor(logging.DEBUG):

                              log.debug(

                                  "\tunencountered children, putting remainder of snippet aside"
@@ -520,6 +533,7 @@ 

                                      if child not in commit_coroutines

                                  ],

                              )

+ 

                          break

  

                      # For all visitor coroutines, merge their produced info, e.g. to determine if
@@ -539,7 +553,7 @@ 

                          info["child_must_continue"] for info in children_visitors_info

                      )

  

-                 branch.append(commit)

+                 snippet.append(commit)

  

                  if keep_processing:

                      # Create visitor coroutines for the commit from the functions passed into this
@@ -553,7 +567,9 @@ 

                      # the results.

                      commit_coroutines_info[commit] = [next(c) for c in coroutines]

                  else:

-                     # Only traverse this commit.

+                     # Only traverse this commit. Traversal is important if parent commits are the

+                     # root of branches that affect the results (computed release number and

+                     # generated changelog).

                      commit_coroutines[commit] = coroutines = None

                      commit_coroutines_info[commit] = [

                          {"child_must_continue": False} for v in visitors
@@ -577,31 +593,37 @@ 

                  log.debug("\tparent to follow: %s", commit.short_id)

  

          ###########################################################################################

-         # Now, `branches` contains disjunct lists of commits in new -> old order. Process these in

+         # Now, `snippets` contains disjunct lists of commits in new -> old order. Process these in

          # reverse, one at a time until encountering a commit where we don't know the results of all

          # parents. Then put the remainder back on the stack to be further processed later until we

-         # run out of branches with commits.

+         # run out of snippets with commits.

+         #

+         # Here, the “bottom halves” of visitors get results from their parent commit(s) as well as

+         # visitors run prior on the same commit, i.e. `release_number_visitor()` ->

+         # `changelog_visitor()`.

          ###########################################################################################

  

          log.debug("=====================================")

          log.debug("Processing linear history snippets...")

          log.debug("=====================================")

  

+         # This maps commits to their results.

          visited_results = {}

  

-         while branches:

-             branch = branches.pop(0)

-             if branch:

-                 log.debug("Processing snippet %s", branch[0].short_id)

-             while branch:

-                 # Take one commit from the tail end of the branch and process.

-                 commit = branch.pop()

+         while snippets:

+             snippet = snippets.pop(0)

+             if snippet:

+                 log.debug("Processing snippet %s", snippet[0].short_id)

+             while snippet:

+                 # Take one commit from the tail end of the snippet and process.

+                 commit = snippet.pop()

  

                  if log.isEnabledFor(logging.DEBUG):

                      log.debug("commit %s: %s", commit.short_id, commit.message.split("\n", 1)[0])

  

                  if commit_coroutines[commit] is None:

-                     # Only traverse, don't process commit.

+                     # Only traverse, but don't process this commit. Ancestral commits might have to

+                     # be taken into account again, so we can’t simply stop here.

                      log.debug("\tonly traverse")

                      continue

  
@@ -613,15 +635,15 @@ 

                          log.debug(

                              "\tcommit_coroutines[p] is None: %s", commit_coroutines[p] is None

                          )

+ 

                  if not all(

                      p in visited_results or commit_coroutines[p] is None for p in commit.parents

                  ):

+                     # This commit doesn’t have all information it needs to be processed. Put it and

+                     # the remainder of the snippet back to be processed later.

                      log.debug("\tputting back")

-                     # put the unprocessed commit back

-                     branch.append(commit)

-                     # put the unprocessed remainder back

-                     branches.append(branch)

- 

+                     snippet.append(commit)

+                     snippets.append(snippet)

                      break

  

                  parent_results = [visited_results.get(p, {}) for p in commit.parents]

file modified
+1 -1
@@ -1,6 +1,6 @@ 

  [tox]

  minversion = 3.6.0

- envlist = py{36,38,310}

+ envlist = py{36,38,310,311}

  isolated_build = true

  skip_missing_interpreters = true

  

Fixes: #289

Also, don't run CI on Fedora 35 anymore, additionally configure tox for Python 3.11.

Build succeeded.

Pull-Request has been merged by nphilipp

a year ago

Build succeeded.