#5508 Bump test containers to F40, bump pip version pinning to align with rpm versions, address some tech debts in dependencies
Merged 13 days ago by ngompa. Opened 3 months ago by wombelix.
wombelix/pagure tests_bump_to_f40  into  master

@@ -1,4 +1,4 @@ 

- FROM quay.io/fedora/fedora:39-x86_64

+ FROM quay.io/fedora/fedora:40-x86_64

  

  LABEL org.opencontainers.image.authors="pagure community"

  LABEL org.opencontainers.image.url="https://pagure.io/pagure"

@@ -1,4 +1,4 @@ 

- FROM quay.io/fedora/fedora:39-x86_64

+ FROM quay.io/fedora/fedora:40-x86_64

  

  LABEL org.opencontainers.image.authors="pagure community"

  LABEL org.opencontainers.image.url="https://pagure.io/pagure"

@@ -1,4 +1,4 @@ 

- FROM quay.io/fedora/fedora:39-x86_64

+ FROM quay.io/fedora/fedora:40-x86_64

  

  ARG repo=https://pagure.io/pagure.git

  ARG branch=master

@@ -1,4 +1,4 @@ 

- FROM quay.io/fedora/fedora:39-x86_64

+ FROM quay.io/fedora/fedora:40-x86_64

  

  ARG repo=https://pagure.io/pagure.git

  ARG branch=master

@@ -169,6 +169,15 @@ 

               "branch in current directory, can also be overridden by "

               "using the BRANCH environment variable",

      )

+     parser.add_argument(

+         "--specfile",

+         dest="specfile",

+         default="https://pagure.io/pagure/raw/master/f/files/pagure.spec",

+         help="Specfile to use when installing RPM packages during container build. "

+              "Defaults to https://pagure.io/pagure/raw/master/f/files/pagure.spec. "

+              "Can also be overridden by using the SPECFILE environment variable "

+              "Use spec file from current pagure folder: file:///wrkdir/files/pagure.spec",

+     )

  

      return parser

  
@@ -261,6 +270,7 @@ 

                  container_volume,

                  branch="{}".format(os.environ.get("BRANCH") or args.branch),

                  repo="{}".format(os.environ.get("REPO") or args.repo),

+                 specfile="{}".format(os.environ.get("SPECFILE") or args.specfile),

              ):

                  base_build = True

              else:

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

  Requires:           python%{python_pkgversion}-flask-oidc

  Requires:           python%{python_pkgversion}-kitchen

  Requires:           python%{python_pkgversion}-markdown

+ Requires:           python%{python_pkgversion}-markupsafe

  Requires:           python%{python_pkgversion}-munch

  Requires:           python%{python_pkgversion}-pillow

  Requires:           python%{python_pkgversion}-psutil

file modified
+5 -3
@@ -701,9 +701,11 @@ 

      if not ((is_repo_committer(repo) and request.allow_rebase) or can_rebase):

          raise pagure.exceptions.APIError(

              403,

-             error_code=APIERROR.EREBASENOTALLOWED

-             if not request.allow_rebase

-             else APIERROR.ENOPRCLOSE,

+             error_code=(

+                 APIERROR.EREBASENOTALLOWED

+                 if not request.allow_rebase

+                 else APIERROR.ENOPRCLOSE

+             ),

          )

  

      task = pagure.lib.tasks.rebase_pull_request.delay(

file modified
+1 -1
@@ -294,7 +294,7 @@ 

  

      output["plugins"] = []

  

-     for (plugin, dbobj) in plugins.items():

+     for plugin, dbobj in plugins.items():

          if dbobj:

              form = plugin.form(obj=dbobj)

              fields = _filter_fields(plugin)

file modified
+9 -7
@@ -545,10 +545,10 @@ 

      # Get the explicit watch statuses

      for watcher in repo.watchers:

          if watcher.watch_issues or watcher.watch_commits:

-             watching_users_to_watch_level[

-                 watcher.user.username

-             ] = pagure.lib.query.get_watch_level_on_repo(

-                 flask.g.session, watcher.user.username, repo

+             watching_users_to_watch_level[watcher.user.username] = (

+                 pagure.lib.query.get_watch_level_on_repo(

+                     flask.g.session, watcher.user.username, repo

+                 )

              )

          else:

              if watcher.user.username in watching_users_to_watch_level:
@@ -1228,9 +1228,11 @@ 

              {

                  "name": p.name,

                  "namespace": p.namespace,

-                 "fullname": p.fullname.replace("forks/", "fork/", 1)

-                 if p.fullname.startswith("forks/")

-                 else p.fullname,

+                 "fullname": (

+                     p.fullname.replace("forks/", "fork/", 1)

+                     if p.fullname.startswith("forks/")

+                     else p.fullname

+                 ),

                  "description": p.description,

              }

              for p in projects

file modified
+1 -1
@@ -893,7 +893,7 @@ 

              session.add(user)

              session.commit()

  

-     print(f"\n\n# Activity by {user.user} that needs to be removed manually:")

+     print(f"\n\n# Activity by {user.user} that needs to be removed manually: ")

  

      print("## Issue Comments")

      if comments:

file modified
+3 -3
@@ -34,9 +34,9 @@ 

      if config["ENABLE_DOCS"]:

          config["DOCS_FOLDER"] = os.path.join(config["GIT_FOLDER"], "docs")

      else:

-         config[

-             "DOCS_FOLDER"

-         ] = None  # Avoid 'KeyError' Exception down the line

+         config["DOCS_FOLDER"] = (

+             None  # Avoid 'KeyError' Exception down the line

+         )

      if config["ENABLE_TICKETS"]:

          config["TICKETS_FOLDER"] = os.path.join(

              config["GIT_FOLDER"], "tickets"

file modified
+3 -1
@@ -18,6 +18,8 @@ 

  from binaryornot.helpers import is_binary_string

  from whitenoise import WhiteNoise

  

+ import markupsafe

+ 

  import pagure.config

  import pagure.doc_utils

  import pagure.exceptions
@@ -188,7 +190,7 @@ 

      else:

          flask.abort(

              404,

-             flask.Markup(

+             markupsafe.Markup(

                  "No content found in the repository, you may want to read "

                  'the <a href="'

                  'https://docs.pagure.org/pagure/usage/using_doc.html">'

file modified
+12 -1
@@ -458,8 +458,19 @@ 

          # execution will go through the else clause, making the Pagure

          # authentication machinery pick the user up

          if not oidc.user_loggedin:

-             return oidc.redirect_to_auth_server(flask.request.url)

+             # Available in flask-oidc < 2.0.0

+             if "redirect_to_auth_server" in dir(oidc):

+                 return oidc.redirect_to_auth_server(flask.request.url)

+             else:

+                 # flask-oidc >= 2.0.0 offers decorator 'oidc.require_login'

+                 # and removed method 'redirect_to_auth_server'

+                 @oidc.require_login

+                 def _oidc_redirect_to_auth_server():

+                     pass

+ 

+                 return _oidc_redirect_to_auth_server()

          else:

+             flask.g.test["test"] = "oidc.user_loggedin - TRUE"

              flask.session["oidc_logintime"] = time.time()

              fas_user_from_oidc()

              authenticated = pagure.utils.authenticated()

file modified
+1 -1
@@ -609,7 +609,7 @@ 

  

      ssh_key = wtforms.StringField(

          "SSH Key",

-         [wtforms.validators.DataRequired()]

+         [wtforms.validators.DataRequired()],

          # TODO: Add an ssh key validator?

      )

  

file modified
+10 -6
@@ -141,9 +141,11 @@ 

      msg = dict(

          authors=[author],

          agent=user,

-         repo=project.to_json(public=True)

-         if not isinstance(project, six.string_types)

-         else project,

+         repo=(

+             project.to_json(public=True)

+             if not isinstance(project, six.string_types)

+             else project

+         ),

      )

      if subject == "branch":

          msg["branch"] = refname
@@ -209,9 +211,11 @@ 

              authors=list(authors),

              changed_files=changed_files,

              agent=user,

-             repo=project.to_json(public=True)

-             if not isinstance(project, six.string_types)

-             else project,

+             repo=(

+                 project.to_json(public=True)

+                 if not isinstance(project, six.string_types)

+                 else project

+             ),

              pull_request_id=pr_id,

          )

  

file modified
+1 -1
@@ -18,7 +18,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  from pagure.hooks import BaseHook, BaseRunner

  from pagure.lib.model import BASE, Project

file modified
+1 -1
@@ -19,7 +19,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  from pagure.hooks import BaseHook, RequiredIf

  from pagure.lib.model import BASE, Project

file modified
+1 -1
@@ -22,7 +22,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  from pagure.config import config as pagure_config

  from pagure.hooks import BaseHook, BaseRunner, RequiredIf

file modified
+1 -1
@@ -17,7 +17,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure.config

  import pagure.lib.tasks_mirror

file modified
+1 -1
@@ -19,7 +19,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure.lib.login

  from pagure.hooks import BaseHook, BaseRunner, RequiredIf

@@ -18,7 +18,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure.lib.git

  from pagure.hooks import BaseHook, BaseRunner, RequiredIf

file modified
+1 -1
@@ -22,7 +22,7 @@ 

      from flask_wtf import Form as FlaskForm

  

  from sqlalchemy.exc import SQLAlchemyError

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure.config

  import pagure.lib.git

@@ -18,7 +18,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  from pagure.hooks import BaseHook, BaseRunner

  from pagure.lib.model import BASE, Project

@@ -18,7 +18,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure.lib.git

  import pagure.lib.tasks_services

@@ -20,7 +20,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure.lib.git

  import pagure.lib.tasks_services

@@ -18,7 +18,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure.config

  import pagure.lib.git

file modified
+1 -1
@@ -20,7 +20,7 @@ 

  except ImportError:

      from flask_wtf import Form as FlaskForm

  

- from sqlalchemy.orm import backref, relation

+ from sqlalchemy.orm import backref, relationship as relation

  

  import pagure

  from pagure.hooks import BaseHook, BaseRunner

file modified
+5 -3
@@ -1596,9 +1596,11 @@ 

                      "Branch %s could not be found in the repo %s"

                      % (

                          request.branch_from,

-                         request.project_from.fullname

-                         if request.project_from

-                         else request.remote_git,

+                         (

+                             request.project_from.fullname

+                             if request.project_from

+                             else request.remote_git

+                         ),

                      )

                  )

  

file modified
+1 -1
@@ -66,7 +66,7 @@ 

              "pagure_authorized_keys": PagureGitAuth,

          }[backend]

      else:

-         cls = classes[backend].load()

+         cls = classes[backend].load(False)

      _log.debug("Returning helper %r from backend key %r" % (cls, backend))

  

      GIT_AUTH_BACKEND_NAME = backend

file modified
+38 -30
@@ -26,7 +26,7 @@ 

  from sqlalchemy.exc import SQLAlchemyError

  from sqlalchemy.orm import (

      backref,

-     relation,

+     relationship as relation,

      scoped_session,

      sessionmaker,

      validates,
@@ -1039,9 +1039,11 @@ 

              "full_url": self.full_url,

              "description": self.description,

              "namespace": self.namespace,

-             "parent": self.parent.to_json(public=public, api=api)

-             if self.parent

-             else None,

+             "parent": (

+                 self.parent.to_json(public=public, api=api)

+                 if self.parent

+                 else None

+             ),

              "date_created": arrow_ts(self.date_created),

              "date_modified": arrow_ts(self.date_modified),

              "user": self.user.to_json(public=public),
@@ -1507,20 +1509,22 @@ 

              "tags": self.tags_text,

              "depends": ["%s" % item for item in self.depending_text],

              "blocks": ["%s" % item for item in self.blocking_text],

-             "assignee": self.assignee.to_json(public=public)

-             if self.assignee

-             else None,

+             "assignee": (

+                 self.assignee.to_json(public=public) if self.assignee else None

+             ),

              "priority": self.priority,

              "milestone": self.milestone,

              "custom_fields": custom_fields,

-             "closed_by": self.closed_by.to_json(public=public)

-             if self.closed_by

-             else None,

-             "related_prs": [

-                 {"id": pr.id, "title": pr.title} for pr in self.related_prs

-             ]

-             if self.related_prs

-             else [],

+             "closed_by": (

+                 self.closed_by.to_json(public=public)

+                 if self.closed_by

+                 else None

+             ),

+             "related_prs": (

+                 [{"id": pr.id, "title": pr.title} for pr in self.related_prs]

+                 if self.related_prs

+                 else []

+             ),

              "full_url": self.full_url,

          }

  
@@ -1683,9 +1687,9 @@ 

              "date_created": arrow_ts(self.date_created),

              "user": self.user.to_json(public=public),

              "edited_on": arrow_ts(self.edited_on) if self.edited_on else None,

-             "editor": self.editor.to_json(public=public)

-             if self.editor_id

-             else None,

+             "editor": (

+                 self.editor.to_json(public=public) if self.editor_id else None

+             ),

              "notification": self.notification,

              "reactions": self.reactions,

          }
@@ -2212,24 +2216,28 @@ 

              "branch": self.branch,

              "project": self.project.to_json(public=public, api=api),

              "branch_from": self.branch_from,

-             "repo_from": self.project_from.to_json(public=public, api=api)

-             if self.project_from

-             else None,

+             "repo_from": (

+                 self.project_from.to_json(public=public, api=api)

+                 if self.project_from

+                 else None

+             ),

              "remote_git": self.remote_git,

              "date_created": arrow_ts(self.date_created),

              "updated_on": arrow_ts(self.updated_on),

              "last_updated": arrow_ts(self.last_updated),

              "closed_at": arrow_ts(self.closed_at) if self.closed_at else None,

              "user": self.user.to_json(public=public),

-             "assignee": self.assignee.to_json(public=public)

-             if self.assignee

-             else None,

+             "assignee": (

+                 self.assignee.to_json(public=public) if self.assignee else None

+             ),

              "status": self.status,

              "commit_start": self.commit_start,

              "commit_stop": self.commit_stop,

-             "closed_by": self.closed_by.to_json(public=public)

-             if self.closed_by

-             else None,

+             "closed_by": (

+                 self.closed_by.to_json(public=public)

+                 if self.closed_by

+                 else None

+             ),

              "initial_comment": self.initial_comment,

              "cached_merge_status": self.merge_status or "unknown",

              "threshold_reached": self.threshold_reached,
@@ -2358,9 +2366,9 @@ 

              "date_created": arrow_ts(self.date_created),

              "user": self.user.to_json(public=public),

              "edited_on": arrow_ts(self.edited_on) if self.edited_on else None,

-             "editor": self.editor.to_json(public=public)

-             if self.editor_id

-             else None,

+             "editor": (

+                 self.editor.to_json(public=public) if self.editor_id else None

+             ),

              "notification": self.notification,

              "reactions": self.reactions,

          }

file modified
+38 -32
@@ -4370,37 +4370,40 @@ 

          else:

              attrs["img"] = filter_img_src

  

-     tags = bleach.ALLOWED_TAGS + [

-         "p",

-         "br",

-         "div",

-         "h1",

-         "h2",

-         "h3",

-         "h4",

-         "h5",

-         "h6",

-         "table",

-         "td",

-         "tr",

-         "th",

-         "thead",

-         "tbody",

-         "col",

-         "pre",

-         "img",

-         "hr",

-         "dl",

-         "dt",

-         "dd",

-         "span",

-         "kbd",

-         "var",

-         "del",

-         "cite",

-         "noscript",

-         "colgroup",

-     ]

+     tags = list(bleach.ALLOWED_TAGS)

+     tags.extend(

+         [

+             "p",

+             "br",

+             "div",

+             "h1",

+             "h2",

+             "h3",

+             "h4",

+             "h5",

+             "h6",

+             "table",

+             "td",

+             "tr",

+             "th",

+             "thead",

+             "tbody",

+             "col",

+             "pre",

+             "img",

+             "hr",

+             "dl",

+             "dt",

+             "dd",

+             "span",

+             "kbd",

+             "var",

+             "del",

+             "cite",

+             "noscript",

+             "colgroup",

+         ]

+     )

      if ignore:

          for tag in ignore:

              if tag in tags:
@@ -4410,7 +4413,10 @@ 

  

      # newer bleach allow to customize the protocol supported

      if tuple(bleach_v) >= (1, 5, 0):  # pragma: no cover

-         protocols = bleach.ALLOWED_PROTOCOLS + ["irc", "ircs"]

+         protocols = list(bleach.ALLOWED_PROTOCOLS)

+         protocols.extend(["irc", "ircs"])

+         print(type(protocols))

+         print(protocols)

          kwargs["protocols"] = protocols

  

      return bleach.clean(text, **kwargs)

@@ -415,7 +415,6 @@ 

      project_name=None,

      pr_uid=None,

  ):

- 

      """Triggers a new run of the CI system on the specified pull-request."""

      pagure.lib.plugins.get_plugin("Pagure CI")

  

file modified
+3 -3
@@ -445,9 +445,9 @@ 

              md.inlinePatterns["striked"] = StrikeThroughPattern(

                  STRIKE_THROUGH_RE

              )

-             md.postprocessors[

-                 "encapsulate"

-             ] = EncapsulateMarkdownPostprocessor()

+             md.postprocessors["encapsulate"] = (

+                 EncapsulateMarkdownPostprocessor()

+             )

  

          def _new_markdown_way():

              idx = md.inlinePatterns.get_index_for_name("autolink")

file modified
+3 -3
@@ -610,9 +610,9 @@ 

                  "ui_ns.view_repo",

                  repo=projects[0].name,

                  namespace=projects[0].namespace,

-                 username=projects[0].user.username

-                 if projects[0].is_fork

-                 else None,

+                 username=(

+                     projects[0].user.username if projects[0].is_fork else None

+                 ),

              )

          )

  

file modified
+1 -1
@@ -15,7 +15,7 @@ 

  

  import flask

  import flask_fas_openid

- from flask import Markup

+ from markupsafe import Markup

  from sqlalchemy.exc import SQLAlchemyError

  

  import pagure.lib.query

file modified
+10 -6
@@ -242,9 +242,11 @@ 

                          flask.url_for(

                              "ui_ns.view_file",

                              repo=prequest.project_from.name,

-                             username=prequest.project_from.user.username

-                             if prequest.project_from.is_fork

-                             else None,

+                             username=(

+                                 prequest.project_from.user.username

+                                 if prequest.project_from.is_fork

+                                 else None

+                             ),

                              namespace=prequest.project_from.namespace,

                              identifier=prequest.branch_from,

                              filename=filename,
@@ -328,9 +330,11 @@ 

                                  namespace=comment.parent.project.namespace,

                                  requestid=comment.parent.id,

                                  commentid=comment.id,

-                                 username=comment.parent.user.user

-                                 if comment.parent.project.is_fork

-                                 else None,

+                                 username=(

+                                     comment.parent.user.user

+                                     if comment.parent.project.is_fork

+                                     else None

+                                 ),

                              ),

                              "requestid": comment.parent.id,

                              "commentid": comment.id,

file modified
+7 -1
@@ -15,7 +15,7 @@ 

  

  import flask

  import munch

- from flask import Markup

+ from markupsafe import Markup

  from flask_oidc import OpenIDConnect

  from sqlalchemy.exc import SQLAlchemyError

  
@@ -43,10 +43,16 @@ 

              pagure_config["OIDC_PAGURE_SSH_KEY"],

              pagure_config["OIDC_PAGURE_GROUPS"],

          ]

+ 

+         # flask-oidc >= 2.0.0, < 3.0.0

+         # "The user_getinfo method is deprecated

+         # please use session['oidc_auth_profile']"

+         # Requires attention on next major release

          info = oidc.user_getinfo(

              [email_key, fulln_key, usern_key, ssh_key, groups_key]

          )

          username = info.get(usern_key)

+ 

          if not username:

              fb = pagure_config["OIDC_PAGURE_USERNAME_FALLBACK"]

              if fb == "email":

file modified
+1 -1
@@ -16,7 +16,7 @@ 

  import logging

  

  import flask

- from flask import Markup

+ from markupsafe import Markup

  from sqlalchemy.exc import SQLAlchemyError

  

  import pagure.exceptions

file modified
+7 -4
@@ -28,6 +28,7 @@ 

  

  import flask

  import kitchen.text.converters as ktc

+ import markupsafe

  import pygit2

  import six

  import werkzeug.utils
@@ -2343,9 +2344,11 @@ 

              pagure.lib.query.add_token_to_user(

                  flask.g.session,

                  repo,

-                 description=form.description.data.strip()

-                 if form.description.data

-                 else None,

+                 description=(

+                     form.description.data.strip()

+                     if form.description.data

+                     else None

+                 ),

                  acls=form.acls.data,

                  username=flask.g.fas_user.username,

                  expiration_date=form.expiration_date.data,
@@ -2487,7 +2490,7 @@ 

          except SQLAlchemyError as err:  # pragma: no cover

              flask.g.session.rollback()

              _log.exception(err)

-             message = flask.Markup(

+             message = markupsafe.Markup(

                  "Token could not be revoked,"

                  ' please <a href="/about">contact an administrator</a>'

              )

file modified
+2 -1
@@ -19,6 +19,7 @@ 

  from functools import wraps

  

  import flask

+ import markupsafe

  import pygit2

  import six

  import werkzeug.utils
@@ -385,7 +386,7 @@ 

          elif auth_method == "fas" and not flask.g.fas_user.cla_done:

              flask.session["_requires_fpca"] = True

              flask.flash(

-                 flask.Markup(

+                 markupsafe.Markup(

                      'You must <a href="https://accounts.fedoraproject'

                      '.org/">sign the FPCA</a> (Fedora Project '

                      "Contributor Agreement) to use pagure"

file modified
+2 -2
@@ -1,2 +1,2 @@ 

- cryptography <= 36.0.0

- python-jenkins <= 1.7.0

+ cryptography <= 41.0.7

+ python-jenkins <= 1.8.2

file modified
+8 -10
@@ -1,15 +1,13 @@ 

- bcrypt <= 3.2.2

- beautifulsoup4 <= 4.11.1

- black <= 22.8.0

- cryptography <= 36.0.0

- eventlet <= 0.33.2

+ beautifulsoup4 <= 4.12.3

+ black <= 24.4.2

+ eventlet <= 0.35.1

  fedmsg <= 1.1.2

- flake8

+ flake8 <= 6.0.0

  

- # Important: Until https://github.com/puiterwijk/flask-oidc/pull/144 is merged and a new version of flask-oidc is released,

- # it's necessary to ensure `itsdangerous` is pinned to a version lower as 2.1 as well.

- flask-oidc <= 1.4.0

- itsdangerous < 2.1          

+ # fedora-infra maintains its own flask-oidc fork: https://github.com/fedora-infra/flask-oidc

+ # previous upstream PR (https://github.com/puiterwijk/flask-oidc/pull/144) fixed in fork.

+ flask-oidc <= 2.1.1

+ itsdangerous >=2.1.2

  

  mock <= 4.0.3

  pagure-messages >= 0.0.1

file modified
+24 -22
@@ -1,51 +1,53 @@ 

  # Used for when working from a virtualenv.

  # Use this file by running "$ pip install -r requirements.txt"

- alembic <= 1.8.1

+ alembic <= 1.13.2

  arrow <= 1.2.3

- bcrypt <= 3.2.2

+ bcrypt <= 4.0.1

  binaryornot == 0.4.4

- bleach <= 5.0.1

- blinker <= 1.5

- celery

- chardet <= 4.0.0

- cryptography <= 36.0.0

- docutils <= 0.17.1

+ bleach <= 6.1.0

+ blinker <= 1.7.0

+ celery <= 5.3.5

+ chardet <= 5.2.0

+ cryptography <= 41.0.7

+ docutils <= 0.20.1

  

  # required for backward compatibility

  email_validator

  

- flask <= 2.2.2

- werkzeug < 3.0.0

- flask-wtf <= 1.0.0

+ flask <= 3.0.3

+ werkzeug <= 3.0.3

+ flask-wtf <= 1.2.1

  kitchen == 1.2.6

- markdown

+ markdown <= 3.5.2

  munch <= 2.5.0

- Pillow <= 9.2.0

- psutil <= 5.9.2

+ Pillow <= 10.3.0

+ psutil <= 5.9.8

  pygit2 < 1.15.0

  python3-openid <= 3.2.0

  python-openid-cla == 1.2

  python-openid-teams == 1.1

  redis

- requests <= 2.28.1

+ requests <= 2.31.0

  six <= 1.16.0

  

  # required for backward compatibility

  setuptools

  

- sqlalchemy < 2.0.0

+ sqlalchemy <= 2.0.34

  straight.plugin

- whitenoise <= 6.2.0

+ whitenoise <= 6.4.0

  wtforms <= 3.0.1

  

+ markupsafe

+ 

  # Required only for the `oidc` authentication backend

- # Important: Until https://github.com/puiterwijk/flask-oidc/pull/144 is merged and a new version of flask-oidc is released, 

- # it's necessary to ensure `itsdangerous` is pinned to a version lower as 2.1 as well.

- # flask-oidc <= 1.4.0

- # itsdangerous < 2.1

+ # fedora-infra maintains its own flask-oidc fork: https://github.com/fedora-infra/flask-oidc

+ # previous upstream PR (https://github.com/puiterwijk/flask-oidc/pull/144) fixed in fork.

+ # flask-oidc <= 2.1.1

+ # itsdangerous >=2.1.2

  

  # Required only if `USE_FLASK_SESSION_EXT` is set to `True`

- # flask-session

+ # flask-session <= 0.5.0

  

  # Required only for the `fas` and `openid` authentication backends

  # python-fedora == 1.1.1

@@ -37,10 +37,8 @@ 

      """

  

      @patch("pagure.lib.notify.send_email")

-     @patch("pagure.lib.git._maybe_wait")

-     def test_dumping_reloading_ticket(self, mw, send_email):

+     def test_dumping_reloading_ticket(self, send_email):

          """Test dumping a ticket into a JSON blob."""

-         mw.side_effect = lambda result: result.get()

          send_email.return_value = True

  

          tests.create_projects(self.session)

@@ -729,9 +729,9 @@ 

              self.assertIn("SSH key invalid", output_text)

  

              # Next up, multiple SSH keys

-             data[

-                 "ssh_key"

-             ] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             data["ssh_key"] = (

+                 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             )

              output = self.app.post(

                  "/settings/usersettings/addkey",

                  data=data,
@@ -742,9 +742,9 @@ 

              self.assertIn("Please add single SSH keys.", output_text)

  

              # Now, a valid SSH key

-             data[

-                 "ssh_key"

-             ] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             data["ssh_key"] = (

+                 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             )

              output = self.app.post(

                  "/settings/usersettings/addkey",

                  data=data,
@@ -769,9 +769,9 @@ 

              self.assertIn("SSH key already exists", output_text)

  

              # And next, a key with push access

-             data[

-                 "ssh_key"

-             ] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9Xwc2RDzPBhlEDARfHldGjudIVoa04tqT1JVKGQmyllTFz7Rb8CngQL3e7zyNzotnhwYKHdoiLlPkVEiDee4dWMUe48ilqId+FJZQGhyv8fu4BoFdE1AJUVylzmltbLg14VqG5gjTpXgtlrEva9arKwBMHJjRYc8ScaSn3OgyQw=="

+             data["ssh_key"] = (

+                 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9Xwc2RDzPBhlEDARfHldGjudIVoa04tqT1JVKGQmyllTFz7Rb8CngQL3e7zyNzotnhwYKHdoiLlPkVEiDee4dWMUe48ilqId+FJZQGhyv8fu4BoFdE1AJUVylzmltbLg14VqG5gjTpXgtlrEva9arKwBMHJjRYc8ScaSn3OgyQw=="

+             )

              output = self.app.post(

                  "/settings/usersettings/addkey",

                  data=data,

@@ -39,6 +39,7 @@ 

          "token_uri": "dummy-uri://",

          "userinfo_uri": "dummy-uri://",

          "redirect_uris": ["http://localhost:5000/oidc"],

+         "issuer": "http://localhost:5000/oidc/",  # Additional field, required since flask-oidc v2.0.0

      }

  }

  
@@ -75,19 +76,24 @@ 

          with open(secrets_path, "w") as secrets:

              secrets.write(json.dumps(CLIENT_SECRETS))

  

-         oidc.init_app(self.app)

- 

          self.request_context = self.app.test_request_context("/")

          self.request_context.push()

          flask.session["oidc_logintime"] = "dummy-logintime"

          flask.g.session = Mock()  # the DB session should be here

-         flask.g.oidc_id_token = {"sub": "dummy"}

+ 

+         flask.g.oidc_id_token = {"sub": "dummy"}  # Used in flask-oidc < 2.0.0

+         flask.session["oidc_auth_token"] = {

+             "sub": "dummy"

+         }  # Used in flask-oidc >= 2.0.0

+ 

          self.user_info = {

              "email": "dummy@example.com",

              "name": "Dummy User",

              "preferred_username": "dummy",

          }

  

+         oidc.init_app(self.app)

+ 

      def tearDown(self):

          self.request_context.pop()

          self.config_patcher.stop()
@@ -95,7 +101,12 @@ 

      def test_fas_user_from_oidc(self):

          """Test the user creation function."""

          user_info = self.user_info.copy()

-         flask.g._oidc_userinfo = user_info

+ 

+         flask.g._oidc_userinfo = user_info  # Used in flask-oidc < 2.0.0

+         flask.session["oidc_auth_profile"] = (

+             user_info  # Used in flask-oidc >= 2.0.0

+         )

+ 

          fas_user_from_oidc()

          self.assertIsNotNone(getattr(flask.g, "fas_user", None))

          self.assertEqual(flask.g.fas_user.username, "dummy")
@@ -107,7 +118,12 @@ 

          """Test the user creation function."""

          user_info = self.user_info.copy()

          user_info["groups"] = ["group1", "group2"]

-         flask.g._oidc_userinfo = user_info

+ 

+         flask.g._oidc_userinfo = user_info  # Used in flask-oidc < 2.0.0

+         flask.session["oidc_auth_profile"] = (

+             user_info  # Used in flask-oidc >= 2.0.0

+         )

+ 

          fas_user_from_oidc()

          self.assertEqual(flask.g.fas_user.groups, ["group1", "group2"])

  
@@ -115,7 +131,12 @@ 

          """Test the user creation function."""

          user_info = self.user_info.copy()

          user_info["ssh_key"] = "dummy ssh key"

-         flask.g._oidc_userinfo = user_info

+ 

+         flask.g._oidc_userinfo = user_info  # Used in flask-oidc < 2.0.0

+         flask.session["oidc_auth_profile"] = (

+             user_info  # Used in flask-oidc >= 2.0.0

+         )

+ 

          fas_user_from_oidc()

          self.assertEqual(flask.g.fas_user.ssh_key, "dummy ssh key")

  
@@ -123,7 +144,12 @@ 

          """The SSH key may be base64-encoded"""

          user_info = self.user_info.copy()

          user_info["ssh_key"] = "ZHVtbXkgc3NoIGtleQ=="

-         flask.g._oidc_userinfo = user_info

+ 

+         flask.g._oidc_userinfo = user_info  # Used in flask-oidc < 2.0.0

+         flask.session["oidc_auth_profile"] = (

+             user_info  # Used in flask-oidc >= 2.0.0

+         )

+ 

          fas_user_from_oidc()

          self.assertEqual(flask.g.fas_user.ssh_key, "dummy ssh key")

  

@@ -174,9 +174,9 @@ 

              self.assertIn("SSH key invalid", output_text)

  

              # Next up, multiple SSH keys

-             data[

-                 "ssh_key"

-             ] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             data["ssh_key"] = (

+                 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             )

              output = self.app.post(

                  "/test/adddeploykey", data=data, follow_redirects=True

              )
@@ -185,9 +185,9 @@ 

              self.assertIn("Please add single SSH keys.", output_text)

  

              # Now, a valid SSH key

-             data[

-                 "ssh_key"

-             ] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             data["ssh_key"] = (

+                 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q=="

+             )

              output = self.app.post(

                  "/test/adddeploykey", data=data, follow_redirects=True

              )
@@ -212,9 +212,9 @@ 

              self.assertIn("SSH key already exists", output_text)

  

              # And next, a key with push access

-             data[

-                 "ssh_key"

-             ] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9Xwc2RDzPBhlEDARfHldGjudIVoa04tqT1JVKGQmyllTFz7Rb8CngQL3e7zyNzotnhwYKHdoiLlPkVEiDee4dWMUe48ilqId+FJZQGhyv8fu4BoFdE1AJUVylzmltbLg14VqG5gjTpXgtlrEva9arKwBMHJjRYc8ScaSn3OgyQw=="

+             data["ssh_key"] = (

+                 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9Xwc2RDzPBhlEDARfHldGjudIVoa04tqT1JVKGQmyllTFz7Rb8CngQL3e7zyNzotnhwYKHdoiLlPkVEiDee4dWMUe48ilqId+FJZQGhyv8fu4BoFdE1AJUVylzmltbLg14VqG5gjTpXgtlrEva9arKwBMHJjRYc8ScaSn3OgyQw=="

+             )

              data["pushaccess"] = "true"

              output = self.app.post(

                  "/test/adddeploykey", data=data, follow_redirects=True

file modified
+5 -3
@@ -4288,9 +4288,11 @@ 

              # and the version 1.4.3 that we have won't let us adjust the

              # list of supported protocols

              # '<p><a href="ircs://pagure.io">ircs://pagure.io</a></p>',

-             '<div class="markdown"><p><a href="ircs://pagure.io">ircs://pagure.io</a></p></div>'

-             if tuple(bleach_v) >= (1, 5, 0)

-             else '<div class="markdown"><p><a>ircs://pagure.io</a></p></div>',

+             (

+                 '<div class="markdown"><p><a href="ircs://pagure.io">ircs://pagure.io</a></p></div>'

+                 if tuple(bleach_v) >= (1, 5, 0)

+                 else '<div class="markdown"><p><a>ircs://pagure.io</a></p></div>'

+             ),

              # 'http://pagure.io'

              '<div class="markdown"><p><a href="http://pagure.io">http://pagure.io</a></p></div>',

              # 'https://pagure.io'

@@ -40,7 +40,7 @@ 

          """

          Test that strings that could be UTF-8 or ISO-8859-* result in UTF-8.

  

-         python-chardet-3.0.4-2.fc27.noarch detects it as ISO-8859-9

+         python-chardet-3.0.4-2.fc27.noarch and above detects it as ISO-8859-9

          python-chardet-2.2.1-1.el7_1.noarch detects it as ISO-8859-2

          """

          data = "Å abata".encode("utf-8")
@@ -50,7 +50,7 @@ 

              self.assertEqual(result, "WINDOWS-1250")

          else:

              self.assertEqual(result, "utf-8")

-             if chardet.__version__[0] in ("3", "4"):

+             if chardet.__version__[0] in ("3", "4", "5"):

                  self.assertEqual(chardet_result["encoding"], "ISO-8859-9")

              else:

                  self.assertEqual(chardet_result["encoding"], "ISO-8859-2")
@@ -75,7 +75,11 @@ 

                  # The first three have different confidence values

                  expexted_list = ["utf-8", "ISO-8859-9", "ISO-8859-1"]

                  # This is the one with the least confidence

-                 self.assertEqual(result[-1].encoding, "windows-1255")

+                 print(result)

+                 if chardet.__version__ >= "5.1.0":

+                     self.assertEqual(result[-1].encoding, "TIS-620")

+                 else:

+                     self.assertEqual(result[-1].encoding, "windows-1255")

                  self.assertListEqual(

                      [encoding.encoding for encoding in result][:3],

                      expexted_list,
@@ -83,30 +87,58 @@ 

  

                  # The values in the middle of the list all have the same confidence

                  # value and can't be sorted reliably: use sets.

-                 expected_list = sorted(

-                     [

-                         "utf-8",

-                         "ISO-8859-9",

-                         "ISO-8859-1",

-                         "MacCyrillic",

-                         "IBM866",

-                         "TIS-620",

-                         "EUC-JP",

-                         "EUC-KR",

-                         "GB2312",

-                         "KOI8-R",

-                         "Big5",

-                         "IBM855",

-                         "ISO-8859-7",

-                         "SHIFT_JIS",

-                         "windows-1253",

-                         "CP949",

-                         "EUC-TW",

-                         "ISO-8859-5",

-                         "windows-1251",

-                         "windows-1255",

-                     ]

-                 )

+                 if chardet.__version__ >= "5.1.0":

+                     expected_list = sorted(

+                         [

+                             "utf-8",

+                             "ISO-8859-9",

+                             "ISO-8859-1",

+                             "MacCyrillic",

+                             "IBM866",

+                             "TIS-620",

+                             "EUC-JP",

+                             "EUC-KR",

+                             "GB2312",

+                             "KOI8-R",

+                             "Big5",

+                             "IBM855",

+                             "ISO-8859-7",

+                             "SHIFT_JIS",

+                             "windows-1253",

+                             "CP949",

+                             "EUC-TW",

+                             "ISO-8859-5",

+                             "windows-1251",

+                             "windows-1255",

+                             "Johab",  # Added in 5.0.0

+                             "MacRoman",  # Added in 5.1.0

+                         ]

+                     )

+                 else:

+                     expected_list = sorted(

+                         [

+                             "utf-8",

+                             "ISO-8859-9",

+                             "ISO-8859-1",

+                             "MacCyrillic",

+                             "IBM866",

+                             "TIS-620",

+                             "EUC-JP",

+                             "EUC-KR",

+                             "GB2312",

+                             "KOI8-R",

+                             "Big5",

+                             "IBM855",

+                             "ISO-8859-7",

+                             "SHIFT_JIS",

+                             "windows-1253",

+                             "CP949",

+                             "EUC-TW",

+                             "ISO-8859-5",

+                             "windows-1251",

+                             "windows-1255",

+                         ]

+                     )

                  self.assertListEqual(

                      sorted(set([encoding.encoding for encoding in result])),

                      expected_list,

@@ -68,9 +68,11 @@ 

              (

                  "hello",

                  b"#!",

-                 "text/plain; charset=ascii"

-                 if cchardet is None

-                 else "text/plain; charset=ASCII",

+                 (

+                     "text/plain; charset=ascii"

+                     if cchardet is None

+                     else "text/plain; charset=ASCII"

+                 ),

              ),

              ("hello.jpg", None, "image/jpeg"),

              ("hello.jpg", b"#!", "image/jpeg"),

file modified
+1 -1
@@ -1,5 +1,5 @@ 

  [tox]

- envlist = py{39,310,311,312}

+ envlist = py{39,311,312}

  skipsdist = True

  # If the user is missing an interpreter, don't fail

  skip_missing_interpreters = True

Work in Progress PR (not ready to be merged) to bump test container to F40.
This includes raising version pinning in pip and align it with the package version shipped in F40 as rpm. Means it addresses a couple of tech debts as well.
I was able to bring the failed tests down to one:
FAILED tests/test_pagure_flask_dump_load_ticket.py::PagureFlaskDumpLoadTicketTests::test_dumping_reloading_ticket - Exception: Unable to find object
But even after spending hours, I didn't find a way to fix it. For me it looks like an issue how we test and that the actual pagure code is fine.
This update_ticket_from_git call (https://pagure.io/pagure/blob/master/f/tests/test_pagure_flask_dump_load_ticket.py#_194) fails because it the database return is None (https://pagure.io/pagure/blob/master/f/pagure/lib/tasks.py#_277) and not the latest ticket entry (https://pagure.io/pagure/blob/master/f/pagure/lib/query.py#_3507).
The thing is, when I throw an exception before https://pagure.io/pagure/blob/master/f/pagure/lib/tasks.py#_285 like this:

        raise pagure.exceptions.PagureException(
            "session: %s\nticketuid: %s\nobj: %s"
            % (project, ticketuid, obj)
        )

Then obj is not None and contains the expected database result. If I don't throw the exception, then the if obj is None: hits.
That sounds like some sort of weird concurrency, async whatever issue to me. In https://pagure.io/pagure/blob/master/f/tests/test_pagure_flask_dump_load_ticket.py#_172 we throw away the current db session and recreate everything again. But celery seem to continue to use the global one from before because of the task decorator we add: https://pagure.io/pagure/blob/master/f/pagure/lib/tasks_utils.py#_20
But why there is a result when I throw and exception myself but no of I let it just running, no idea yet.

The unit, or better integration tests, starting celery tasks in always eager mode (https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-always-eager). Another confusing thing because we run a redis instance inside the test container and configure the communication through unix sockets. So for probably historical reasons there are a couple of weird things going on regarding testing our celery tasks in my opinion.

So currently I don't have a idea how to further troubleshoot and address the Unable to find object problem. It also becomes a time problem, I spend way too much of my non existing free time on it already :-/ I pushed the code I have so far and leave the PR open as WIP in case someone else has some ideas and can help out here.

pretty please pagure-ci rebuild

2 months ago

1 new commit added

  • tests: Drop mock 'pagure.lib.git._maybe_wait'
13 days ago

CI is still running, py39 passed already, waiting for py311 and p312 to finish.
But local tests on fedora rpm looking good already.

After digging again into the Unable to find object problem I realized that the trace included a mocked method:

tests/test_pagure_flask_dump_load_ticket.py:203: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
pagure/lib/git.py:549: in update_ticket_from_git
    issue = pagure.lib.query.new_issue(
pagure/lib/query.py:1757: in new_issue
    pagure.lib.git.update_git(issue, repo=repo)
pagure/lib/git.py:165: in update_git
    _maybe_wait(queued)
/usr/lib/python3.12/site-packages/mock/mock.py:1100: in __call__
    return _mock_self._mock_call(*args, **kwargs)
/usr/lib/python3.12/site-packages/mock/mock.py:1104: in _mock_call
    return _mock_self._execute_mock_call(*args, **kwargs)
/usr/lib/python3.12/site-packages/mock/mock.py:1167: in _execute_mock_call
    result = effect(*args, **kwargs)
tests/test_pagure_flask_dump_load_ticket.py:43: in <lambda>
    mw.side_effect = lambda result: result.get()
/usr/lib/python3.12/site-packages/celery/result.py:1026: in get
    raise self.result if isinstance(
/usr/lib/python3.12/site-packages/celery/app/trace.py:477: in trace_task
    R = retval = fun(*args, **kwargs)
pagure/lib/tasks_utils.py:36: in decorated_function
    return function(self, session, *args, **kwargs)

Checking out older logs confirms that this trace came up earlier already.

So what does _maybe_wait do? well ...

def _maybe_wait(result):
    """Function to patch if one wants to wait for finish.

    This function should only ever be overridden by a few tests that depend
    on counting and very precise timing."""
    pass

How it's used by other tests?

../tests/__init__.py:def create_maybe_waiter(method, getter):
../tests/__init__.py:        self.app.get = create_maybe_waiter(self.app.get, self.app.get)
../tests/__init__.py:        self.app.post = create_maybe_waiter(self.app.post, self.app.get)
../tests/__init__.py:    """Helper function for definitely waiting in _maybe_wait."""
../tests/test_pagure_flask_dump_load_ticket.py:    @patch("pagure.lib.git._maybe_wait")
../tests/test_pagure_lib_drop_issue.py:    @patch("pagure.lib.git._maybe_wait", tests.definitely_wait)
../tests/test_pagure_lib_drop_issue.py:    @patch("pagure.lib.git._maybe_wait", tests.definitely_wait)
../tests/test_pagure_lib_git.py:        with patch("pagure.lib.git._maybe_wait", tests.definitely_wait):

Twice in tests/test_pagure_lib_drop_issue.py, once in tests/test_pagure_lib_git.py, always with tests.definitely_wait, never in the way it's used in tests/test_pagure_flask_dump_load_ticket.py.
A test with @patch("pagure.lib.git._maybe_wait", tests.definitely_wait) ended up in the same Unable to find object exception.
After just dropping the whole patch / mock:

diff --git a/tests/test_pagure_flask_dump_load_ticket.py b/tests/test_pagure_flask_dump_load_ticket.py
index 828a668a..358adbec 100644
--- a/tests/test_pagure_flask_dump_load_ticket.py
+++ b/tests/test_pagure_flask_dump_load_ticket.py
@@ -37,10 +37,8 @@ class PagureFlaskDumpLoadTicketTests(tests.Modeltests):
     """

     @patch("pagure.lib.notify.send_email")
-    @patch("pagure.lib.git._maybe_wait")
-    def test_dumping_reloading_ticket(self, mw, send_email):
+    def test_dumping_reloading_ticket(self, send_email):
         """Test dumping a ticket into a JSON blob."""
-        mw.side_effect = lambda result: result.get()
         send_email.return_value = True

         tests.create_projects(self.session)

tests/test_pagure_flask_dump_load_ticket.py::PagureFlaskDumpLoadTicketTests::test_dumping_reloading_ticket seem to be happy after all.

I have to admit, I still not fully understanding the reasoning behind how the test was implemented.
My only explanation is, that some Celery behavior must have changed in a (major) release which then hit us after removing the version pinning and using F40.

We have the tests to rely on, and if that one now passes without mocking, I'm fine with that.

:tada:

13:07:30    py39: OK (913.11=setup[28.66]+cmd[884.45] seconds)
13:07:30    py311: OK (906.62=setup[27.10]+cmd[879.52] seconds)
13:07:30    py312: OK (1011.03=setup[51.17]+cmd[959.85] seconds)
13:07:30    congratulations :) (2830.83 seconds)

@ngompa wanna take a look and merge if you are fine with the proposed changes?

Metadata Update from @wombelix:
- Request assigned

13 days ago

Pull-Request has been merged by ngompa

13 days ago
Metadata
Changes Summary 51
+1 -1
file changed
dev/containers/base-fedora-pip-py3
+1 -1
file changed
dev/containers/base-fedora-rpms-py3
+1 -1
file changed
dev/containers/fedora-pip-py3
+1 -1
file changed
dev/containers/fedora-rpms-py3
+10 -0
file changed
dev/run-tests-container.py
+1 -0
file changed
files/pagure.spec
+5 -3
file changed
pagure/api/fork.py
+1 -1
file changed
pagure/api/plugins.py
+9 -7
file changed
pagure/api/project.py
+1 -1
file changed
pagure/cli/admin.py
+3 -3
file changed
pagure/config.py
+3 -1
file changed
pagure/docs_server.py
+12 -1
file changed
pagure/flask_app.py
+1 -1
file changed
pagure/forms.py
+10 -6
file changed
pagure/hooks/default.py
+1 -1
file changed
pagure/hooks/fedmsg_hook.py
+1 -1
file changed
pagure/hooks/irc.py
+1 -1
file changed
pagure/hooks/mail.py
+1 -1
file changed
pagure/hooks/mirror_hook.py
+1 -1
file changed
pagure/hooks/pagure_ci.py
+1 -1
file changed
pagure/hooks/pagure_force_commit.py
+1 -1
file changed
pagure/hooks/pagure_hook.py
+1 -1
file changed
pagure/hooks/pagure_no_new_branches.py
+1 -1
file changed
pagure/hooks/pagure_request_hook.py
+1 -1
file changed
pagure/hooks/pagure_ticket_hook.py
+1 -1
file changed
pagure/hooks/pagure_unsigned_commits.py
+1 -1
file changed
pagure/hooks/rtd.py
+5 -3
file changed
pagure/lib/git.py
+1 -1
file changed
pagure/lib/git_auth.py
+38 -30
file changed
pagure/lib/model.py
+38 -32
file changed
pagure/lib/query.py
+0 -1
file changed
pagure/lib/tasks_services.py
+3 -3
file changed
pagure/pfmarkdown.py
+3 -3
file changed
pagure/ui/app.py
+1 -1
file changed
pagure/ui/fas_login.py
+10 -6
file changed
pagure/ui/filters.py
+7 -1
file changed
pagure/ui/oidc_login.py
+1 -1
file changed
pagure/ui/plugins.py
+7 -4
file changed
pagure/ui/repo.py
+2 -1
file changed
pagure/utils.py
+2 -2
file changed
requirements-ci.txt
+8 -10
file changed
requirements-testing.txt
+24 -22
file changed
requirements.txt
+1 -3
file changed
tests/test_pagure_flask_dump_load_ticket.py
+9 -9
file changed
tests/test_pagure_flask_ui_app.py
+33 -7
file changed
tests/test_pagure_flask_ui_oidc_login.py
+9 -9
file changed
tests/test_pagure_flask_ui_repo.py
+5 -3
file changed
tests/test_pagure_lib.py
+59 -27
file changed
tests/test_pagure_lib_encoding_utils.py
+5 -3
file changed
tests/test_pagure_lib_mimetype.py
+1 -1
file changed
tox.ini