From 34faff4927b2427aae6a4b2efd06d87bd642209e Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 1/27] fix: flask-wtf parameter 'csrf_enabled' deprecated "csrf_enabled" is deprecated since flask-wtf v0.14, removed in v1.0, replaced by "meta={'csrf': }" https://flask-wtf.readthedocs.io/en/1.0.x/changes/#version-0-14 https://github.com/wtforms/flask-wtf/pull/484 --- diff --git a/pagure/api/fork.py b/pagure/api/fork.py index bcfaae2..dc6dceb 100644 --- a/pagure/api/fork.py +++ b/pagure/api/fork.py @@ -504,7 +504,7 @@ def api_pull_request_update(repo, requestid, username=None, namespace=None): request = _get_request(repo, requestid) _check_pull_request_access(request, assignee=True) - form = pagure.forms.RequestPullForm(csrf_enabled=False) + form = pagure.forms.RequestPullForm(meta={'csrf': False}) if not form.validate_on_submit(): raise pagure.exceptions.APIError( 400, error_code=APIERROR.EINVALIDREQ, errors=form.errors @@ -942,7 +942,7 @@ def api_pull_request_add_comment( _check_token(repo, project_token=False) request = _get_request(repo, requestid) - form = pagure.forms.AddPullRequestCommentForm(csrf_enabled=False) + form = pagure.forms.AddPullRequestCommentForm(meta={'csrf': False}) if form.validate_on_submit(): comment = form.comment.data commit = form.commit.data or None @@ -1123,9 +1123,9 @@ def api_pull_request_add_flag(repo, requestid, username=None, namespace=None): request = _get_request(repo, requestid) if "status" in get_request_data(): - form = pagure.forms.AddPullRequestFlagForm(csrf_enabled=False) + form = pagure.forms.AddPullRequestFlagForm(meta={'csrf': False}) else: - form = pagure.forms.AddPullRequestFlagFormV1(csrf_enabled=False) + form = pagure.forms.AddPullRequestFlagFormV1(meta={'csrf': False}) if form.validate_on_submit(): username = form.username.data percent = form.percent.data.strip() or None @@ -1376,7 +1376,7 @@ def api_subscribe_pull_request(repo, requestid, username=None, namespace=None): _check_token(repo) request = _get_request(repo, requestid) - form = pagure.forms.SubscribtionForm(csrf_enabled=False) + form = pagure.forms.SubscribtionForm(meta={'csrf': False}) if form.validate_on_submit(): status = is_true(form.status.data) try: @@ -1549,7 +1549,7 @@ def api_pull_request_create(repo, username=None, namespace=None): _check_pull_request(repo_to) _check_token(repo_from, project_token=False) - form = pagure.forms.RequestPullForm(csrf_enabled=False) + form = pagure.forms.RequestPullForm(meta={'csrf': False}) if not form.validate_on_submit(): raise pagure.exceptions.APIError( 400, error_code=APIERROR.EINVALIDREQ, errors=form.errors @@ -1838,7 +1838,7 @@ def api_pull_request_assign(repo, requestid, username=None, namespace=None): request = _get_request(repo, requestid) _check_pull_request_access(request, assignee=True) - form = pagure.forms.AssignIssueForm(csrf_enabled=False) + form = pagure.forms.AssignIssueForm(meta={'csrf': False}) if form.validate_on_submit(): assignee = form.assignee.data or None # Create our metadata comment object diff --git a/pagure/api/issue.py b/pagure/api/issue.py index 23f50d8..83a73bf 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -168,7 +168,7 @@ def api_new_issue(repo, username=None, namespace=None): form = pagure.forms.IssueFormSimplied( priorities=repo.priorities, milestones=repo.milestones, - csrf_enabled=False, + meta={'csrf': False}, ) if form.validate_on_submit(): title = form.title.data @@ -663,7 +663,7 @@ def api_issue_update(repo, issueid, username=None, namespace=None): issue = _get_issue(repo, issue_id, issueuid=issue_uid) _check_private_issue_access(issue) - form = pagure.forms.IssueFormSimplied(csrf_enabled=False) + form = pagure.forms.IssueFormSimplied(meta={'csrf': False}) if form.validate_on_submit(): title = form.title.data.strip() @@ -837,7 +837,7 @@ def api_change_status_issue(repo, issueid, username=None, namespace=None): status = pagure.lib.query.get_issue_statuses(flask.g.session) form = pagure.forms.StatusForm( - status=status, close_status=repo.close_status, csrf_enabled=False + status=status, close_status=repo.close_status, meta={'csrf': False} ) close_status = None @@ -951,7 +951,7 @@ def api_change_milestone_issue(repo, issueid, username=None, namespace=None): _check_ticket_access(issue, open_access=open_access) form = pagure.forms.MilestoneForm( - milestones=repo.milestones.keys(), csrf_enabled=False + milestones=repo.milestones.keys(), meta={'csrf': False} ) if form.validate_on_submit(): @@ -1049,7 +1049,7 @@ def api_comment_issue(repo, issueid, username=None, namespace=None): issue = _get_issue(repo, issueid) _check_private_issue_access(issue) - form = pagure.forms.CommentForm(csrf_enabled=False) + form = pagure.forms.CommentForm(meta={'csrf': False}) if form.validate_on_submit(): comment = form.comment.data try: @@ -1138,7 +1138,7 @@ def api_assign_issue(repo, issueid, username=None, namespace=None): open_access = repo.settings.get("open_metadata_access_to_all", False) _check_ticket_access(issue, assignee=True, open_access=open_access) - form = pagure.forms.AssignIssueForm(csrf_enabled=False) + form = pagure.forms.AssignIssueForm(meta={'csrf': False}) if form.validate_on_submit(): assignee = form.assignee.data or None # Create our metadata comment object @@ -1236,7 +1236,7 @@ def api_subscribe_issue(repo, issueid, username=None, namespace=None): issue = _get_issue(repo, issueid) _check_private_issue_access(issue) - form = pagure.forms.SubscribtionForm(csrf_enabled=False) + form = pagure.forms.SubscribtionForm(meta={'csrf': False}) if form.validate_on_submit(): status = is_true(form.status.data) try: diff --git a/pagure/api/plugins.py b/pagure/api/plugins.py index baa588a..66f1dd4 100644 --- a/pagure/api/plugins.py +++ b/pagure/api/plugins.py @@ -108,7 +108,7 @@ def api_install_plugin(repo, plugin, username=None, namespace=None): else: dbobj = plugin.db_object() - form = plugin.form(obj=dbobj, csrf_enabled=False) + form = plugin.form(obj=dbobj, meta={'csrf': False}) form.active.data = True for field in plugin.form_fields: fields.append(getattr(form, field)) diff --git a/pagure/api/project.py b/pagure/api/project.py index 1ac8c68..42884bc 100644 --- a/pagure/api/project.py +++ b/pagure/api/project.py @@ -205,7 +205,7 @@ def api_project_tags_new(repo, username=None, namespace=None): repo = _get_repo(repo, username, namespace) _check_token(repo, project_token=False) - form = pagure.forms.ApiAddIssueTagForm(csrf_enabled=False) + form = pagure.forms.ApiAddIssueTagForm(meta={'csrf': False}) if form.validate_on_submit(): tag_name = form.tag.data tag_description = form.tag_description.data @@ -433,7 +433,7 @@ def api_new_git_tags(repo, username=None, namespace=None): flask.request.values.get("with_commits", False) ) - form = pagure.forms.AddGitTagForm(csrf_enabled=False) + form = pagure.forms.AddGitTagForm(meta={'csrf': False}) created = None if form.validate_on_submit(): user_obj = pagure.lib.query.get_user( @@ -1455,7 +1455,7 @@ def api_new_project(): if user: namespaces.extend([grp for grp in user.groups]) - form = pagure.forms.ProjectForm(namespaces=namespaces, csrf_enabled=False) + form = pagure.forms.ProjectForm(namespaces=namespaces, meta={'csrf': False}) if form.validate_on_submit(): name = form.name.data description = form.description.data @@ -1739,7 +1739,7 @@ def api_fork_project(): """ output = {} - form = pagure.forms.ForkRepoForm(csrf_enabled=False) + form = pagure.forms.ForkRepoForm(meta={'csrf': False}) if form.validate_on_submit(): repo = form.repo.data username = form.username.data or None @@ -2433,7 +2433,7 @@ def api_commit_add_flag(repo, commit_hash, username=None, namespace=None): except ValueError: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOCOMMIT) - form = pagure.forms.AddPullRequestFlagForm(csrf_enabled=False) + form = pagure.forms.AddPullRequestFlagForm(meta={'csrf': False}) if form.validate_on_submit(): username = form.username.data percent = form.percent.data.strip() or None @@ -2700,7 +2700,7 @@ def api_modify_acls(repo, namespace=None, username=None): project = _get_repo(repo, username, namespace) _check_token(project, project_token=False) - form = pagure.forms.ModifyACLForm(csrf_enabled=False) + form = pagure.forms.ModifyACLForm(meta={'csrf': False}) if form.validate_on_submit(): acl = form.acl.data group = None @@ -3189,7 +3189,7 @@ def api_project_create_api_token(repo, namespace=None, username=None): ) authorized_acls = pagure_config.get("USER_ACLS", []) - form = pagure.forms.NewTokenForm(csrf_enabled=False, sacls=authorized_acls) + form = pagure.forms.NewTokenForm(meta={'csrf': False}, sacls=authorized_acls) if form.validate_on_submit(): acls = form.acls.data description = form.description.data diff --git a/pagure/internal/__init__.py b/pagure/internal/__init__.py index 9f00343..4c3680d 100644 --- a/pagure/internal/__init__.py +++ b/pagure/internal/__init__.py @@ -226,7 +226,7 @@ def check_ssh_access(): @internal_access_only def pull_request_add_comment(): """Add a comment to a pull-request.""" - pform = pagure.forms.ProjectCommentForm(csrf_enabled=False) + pform = pagure.forms.ProjectCommentForm(meta={'csrf': False}) if not pform.validate_on_submit(): flask.abort(400, description="Invalid request") @@ -240,7 +240,7 @@ def pull_request_add_comment(): if not request: flask.abort(404, description="Pull-request not found") - form = pagure.forms.AddPullRequestCommentForm(csrf_enabled=False) + form = pagure.forms.AddPullRequestCommentForm(meta={'csrf': False}) if not form.validate_on_submit(): flask.abort(400, description="Invalid request") @@ -277,7 +277,7 @@ def pull_request_add_comment(): @internal_access_only def ticket_add_comment(): """Add a comment to an issue.""" - pform = pagure.forms.ProjectCommentForm(csrf_enabled=False) + pform = pagure.forms.ProjectCommentForm(meta={'csrf': False}) if not pform.validate_on_submit(): flask.abort(400, description="Invalid request") @@ -308,7 +308,7 @@ def ticket_add_comment(): "to view it", ) - form = pagure.forms.CommentForm(csrf_enabled=False) + form = pagure.forms.CommentForm(meta={'csrf': False}) if not form.validate_on_submit(): flask.abort(400, description="Invalid request") From 4351d921a5d25f1aeccb03a8667cc1f4176af45d Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 2/27] fix: strip() without testing if var is not None Usage of 'strip()' without testing if var exist / is not None caused "AttributeError: 'NoneType' object has no attribute 'strip'" in various tests --- diff --git a/pagure/api/fork.py b/pagure/api/fork.py index dc6dceb..e1d28a0 100644 --- a/pagure/api/fork.py +++ b/pagure/api/fork.py @@ -1128,7 +1128,7 @@ def api_pull_request_add_flag(repo, requestid, username=None, namespace=None): form = pagure.forms.AddPullRequestFlagFormV1(meta={'csrf': False}) if form.validate_on_submit(): username = form.username.data - percent = form.percent.data.strip() or None + percent = form.percent.data.strip() if form.percent.data else None comment = form.comment.data.strip() url = form.url.data.strip() uid = form.uid.data.strip() if form.uid.data else None diff --git a/pagure/api/project.py b/pagure/api/project.py index 42884bc..d0d316d 100644 --- a/pagure/api/project.py +++ b/pagure/api/project.py @@ -2436,7 +2436,7 @@ def api_commit_add_flag(repo, commit_hash, username=None, namespace=None): form = pagure.forms.AddPullRequestFlagForm(meta={'csrf': False}) if form.validate_on_submit(): username = form.username.data - percent = form.percent.data.strip() or None + percent = form.percent.data.strip() if form.percent.data else None comment = form.comment.data.strip() url = form.url.data.strip() uid = form.uid.data.strip() if form.uid.data else None diff --git a/pagure/ui/fork.py b/pagure/ui/fork.py index 53c707f..3125d0f 100644 --- a/pagure/ui/fork.py +++ b/pagure/ui/fork.py @@ -556,9 +556,9 @@ def request_pull_edit(repo, requestid, username=None, namespace=None): form = pagure.forms.RequestPullEditForm(branches=flask.g.branches) if form.validate_on_submit(): - request.title = form.title.data.strip() - request.initial_comment = form.initial_comment.data.strip() - request.branch = form.branch_to.data.strip() + request.title = form.title.data.strip() if form.title.data else None + request.initial_comment = form.initial_comment.data.strip() if form.initial_comment.data else None + request.branch = form.branch_to.data.strip() if form.branch_to.data else None if flask.g.fas_user.username == request.user.username: request.allow_rebase = form.allow_rebase.data flask.g.session.add(request) @@ -1721,7 +1721,7 @@ def new_request_pull( if orig_commit: orig_commit = orig_commit.oid.hex - initial_comment = form.initial_comment.data.strip() or None + initial_comment = form.initial_comment.data.strip() if form.initial_comment.data else None commit_start = commit_stop = None if diff_commits: commit_stop = diff_commits[0].oid.hex @@ -1887,9 +1887,9 @@ def new_remote_request_pull(repo, username=None, namespace=None): except Exception as err: flask.abort(500, description=err) - branch_from = form.branch_from.data.strip() - branch_to = form.branch_to.data.strip() - remote_git = form.git_repo.data.strip() + branch_from = form.branch_from.data.strip() if form.branch_from.data else None + branch_to = form.branch_to.data.strip() if form.branch_to.data else None + remote_git = form.git_repo.data.strip() if form.git_repo.data else None repopath = pagure.utils.get_remote_repo_path(remote_git, branch_from) if not repopath: @@ -1969,7 +1969,7 @@ def new_remote_request_pull(repo, username=None, namespace=None): user=flask.g.fas_user.username, ) - if form.initial_comment.data.strip() != "": + if form.initial_comment.data and form.initial_comment.data.strip() != "": pagure.lib.query.add_pull_request_comment( flask.g.session, request=request, @@ -2027,7 +2027,7 @@ def new_remote_request_pull(repo, username=None, namespace=None): except pygit2.GitError: branch_to = "master" else: - branch_to = form.branch_to.data.strip() + branch_to = form.branch_to.data.strip() if form.branch_to.data else None return flask.render_template( "remote_pull_request.html", diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py index d46f6ac..4e9853c 100644 --- a/pagure/ui/issues.py +++ b/pagure/ui/issues.py @@ -207,7 +207,7 @@ def update_issue(repo, issueid, username=None, namespace=None): # Check if the optional field is filled if form.assignee.data: assignee = form.assignee.data.strip() - new_status = form.status.data.strip() or None + new_status = form.status.data.strip() if form.status.data else None close_status = form.close_status.data or None if close_status not in repo.close_status: close_status = None @@ -225,7 +225,7 @@ def update_issue(repo, issueid, username=None, namespace=None): new_milestone = None try: if repo.milestones: - new_milestone = form.milestone.data.strip() or None + new_milestone = form.milestone.data.strip() if form.milestone.data else None except AttributeError: pass diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index fcb7f4a..e0a9ca2 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -2374,7 +2374,7 @@ def add_token(repo, username=None, namespace=None): pagure.lib.query.add_token_to_user( flask.g.session, repo, - description=form.description.data.strip() or 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, From c37ea21667201e25952905ead7dc8cde3c472196 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 3/27] fix: flake8 and black unit test errors --- diff --git a/pagure/api/fork.py b/pagure/api/fork.py index e1d28a0..cfffecc 100644 --- a/pagure/api/fork.py +++ b/pagure/api/fork.py @@ -504,7 +504,7 @@ def api_pull_request_update(repo, requestid, username=None, namespace=None): request = _get_request(repo, requestid) _check_pull_request_access(request, assignee=True) - form = pagure.forms.RequestPullForm(meta={'csrf': False}) + form = pagure.forms.RequestPullForm(meta={"csrf": False}) if not form.validate_on_submit(): raise pagure.exceptions.APIError( 400, error_code=APIERROR.EINVALIDREQ, errors=form.errors @@ -942,7 +942,7 @@ def api_pull_request_add_comment( _check_token(repo, project_token=False) request = _get_request(repo, requestid) - form = pagure.forms.AddPullRequestCommentForm(meta={'csrf': False}) + form = pagure.forms.AddPullRequestCommentForm(meta={"csrf": False}) if form.validate_on_submit(): comment = form.comment.data commit = form.commit.data or None @@ -1123,9 +1123,9 @@ def api_pull_request_add_flag(repo, requestid, username=None, namespace=None): request = _get_request(repo, requestid) if "status" in get_request_data(): - form = pagure.forms.AddPullRequestFlagForm(meta={'csrf': False}) + form = pagure.forms.AddPullRequestFlagForm(meta={"csrf": False}) else: - form = pagure.forms.AddPullRequestFlagFormV1(meta={'csrf': False}) + form = pagure.forms.AddPullRequestFlagFormV1(meta={"csrf": False}) if form.validate_on_submit(): username = form.username.data percent = form.percent.data.strip() if form.percent.data else None @@ -1376,7 +1376,7 @@ def api_subscribe_pull_request(repo, requestid, username=None, namespace=None): _check_token(repo) request = _get_request(repo, requestid) - form = pagure.forms.SubscribtionForm(meta={'csrf': False}) + form = pagure.forms.SubscribtionForm(meta={"csrf": False}) if form.validate_on_submit(): status = is_true(form.status.data) try: @@ -1549,7 +1549,7 @@ def api_pull_request_create(repo, username=None, namespace=None): _check_pull_request(repo_to) _check_token(repo_from, project_token=False) - form = pagure.forms.RequestPullForm(meta={'csrf': False}) + form = pagure.forms.RequestPullForm(meta={"csrf": False}) if not form.validate_on_submit(): raise pagure.exceptions.APIError( 400, error_code=APIERROR.EINVALIDREQ, errors=form.errors @@ -1838,7 +1838,7 @@ def api_pull_request_assign(repo, requestid, username=None, namespace=None): request = _get_request(repo, requestid) _check_pull_request_access(request, assignee=True) - form = pagure.forms.AssignIssueForm(meta={'csrf': False}) + form = pagure.forms.AssignIssueForm(meta={"csrf": False}) if form.validate_on_submit(): assignee = form.assignee.data or None # Create our metadata comment object diff --git a/pagure/api/issue.py b/pagure/api/issue.py index 83a73bf..3773951 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -168,7 +168,7 @@ def api_new_issue(repo, username=None, namespace=None): form = pagure.forms.IssueFormSimplied( priorities=repo.priorities, milestones=repo.milestones, - meta={'csrf': False}, + meta={"csrf": False}, ) if form.validate_on_submit(): title = form.title.data @@ -663,7 +663,7 @@ def api_issue_update(repo, issueid, username=None, namespace=None): issue = _get_issue(repo, issue_id, issueuid=issue_uid) _check_private_issue_access(issue) - form = pagure.forms.IssueFormSimplied(meta={'csrf': False}) + form = pagure.forms.IssueFormSimplied(meta={"csrf": False}) if form.validate_on_submit(): title = form.title.data.strip() @@ -837,7 +837,7 @@ def api_change_status_issue(repo, issueid, username=None, namespace=None): status = pagure.lib.query.get_issue_statuses(flask.g.session) form = pagure.forms.StatusForm( - status=status, close_status=repo.close_status, meta={'csrf': False} + status=status, close_status=repo.close_status, meta={"csrf": False} ) close_status = None @@ -951,7 +951,7 @@ def api_change_milestone_issue(repo, issueid, username=None, namespace=None): _check_ticket_access(issue, open_access=open_access) form = pagure.forms.MilestoneForm( - milestones=repo.milestones.keys(), meta={'csrf': False} + milestones=repo.milestones.keys(), meta={"csrf": False} ) if form.validate_on_submit(): @@ -1049,7 +1049,7 @@ def api_comment_issue(repo, issueid, username=None, namespace=None): issue = _get_issue(repo, issueid) _check_private_issue_access(issue) - form = pagure.forms.CommentForm(meta={'csrf': False}) + form = pagure.forms.CommentForm(meta={"csrf": False}) if form.validate_on_submit(): comment = form.comment.data try: @@ -1138,7 +1138,7 @@ def api_assign_issue(repo, issueid, username=None, namespace=None): open_access = repo.settings.get("open_metadata_access_to_all", False) _check_ticket_access(issue, assignee=True, open_access=open_access) - form = pagure.forms.AssignIssueForm(meta={'csrf': False}) + form = pagure.forms.AssignIssueForm(meta={"csrf": False}) if form.validate_on_submit(): assignee = form.assignee.data or None # Create our metadata comment object @@ -1236,7 +1236,7 @@ def api_subscribe_issue(repo, issueid, username=None, namespace=None): issue = _get_issue(repo, issueid) _check_private_issue_access(issue) - form = pagure.forms.SubscribtionForm(meta={'csrf': False}) + form = pagure.forms.SubscribtionForm(meta={"csrf": False}) if form.validate_on_submit(): status = is_true(form.status.data) try: diff --git a/pagure/api/plugins.py b/pagure/api/plugins.py index 66f1dd4..6e7cf96 100644 --- a/pagure/api/plugins.py +++ b/pagure/api/plugins.py @@ -108,7 +108,7 @@ def api_install_plugin(repo, plugin, username=None, namespace=None): else: dbobj = plugin.db_object() - form = plugin.form(obj=dbobj, meta={'csrf': False}) + form = plugin.form(obj=dbobj, meta={"csrf": False}) form.active.data = True for field in plugin.form_fields: fields.append(getattr(form, field)) diff --git a/pagure/api/project.py b/pagure/api/project.py index d0d316d..f156938 100644 --- a/pagure/api/project.py +++ b/pagure/api/project.py @@ -205,7 +205,7 @@ def api_project_tags_new(repo, username=None, namespace=None): repo = _get_repo(repo, username, namespace) _check_token(repo, project_token=False) - form = pagure.forms.ApiAddIssueTagForm(meta={'csrf': False}) + form = pagure.forms.ApiAddIssueTagForm(meta={"csrf": False}) if form.validate_on_submit(): tag_name = form.tag.data tag_description = form.tag_description.data @@ -433,7 +433,7 @@ def api_new_git_tags(repo, username=None, namespace=None): flask.request.values.get("with_commits", False) ) - form = pagure.forms.AddGitTagForm(meta={'csrf': False}) + form = pagure.forms.AddGitTagForm(meta={"csrf": False}) created = None if form.validate_on_submit(): user_obj = pagure.lib.query.get_user( @@ -1455,7 +1455,9 @@ def api_new_project(): if user: namespaces.extend([grp for grp in user.groups]) - form = pagure.forms.ProjectForm(namespaces=namespaces, meta={'csrf': False}) + form = pagure.forms.ProjectForm( + namespaces=namespaces, meta={"csrf": False} + ) if form.validate_on_submit(): name = form.name.data description = form.description.data @@ -1739,7 +1741,7 @@ def api_fork_project(): """ output = {} - form = pagure.forms.ForkRepoForm(meta={'csrf': False}) + form = pagure.forms.ForkRepoForm(meta={"csrf": False}) if form.validate_on_submit(): repo = form.repo.data username = form.username.data or None @@ -2433,7 +2435,7 @@ def api_commit_add_flag(repo, commit_hash, username=None, namespace=None): except ValueError: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOCOMMIT) - form = pagure.forms.AddPullRequestFlagForm(meta={'csrf': False}) + form = pagure.forms.AddPullRequestFlagForm(meta={"csrf": False}) if form.validate_on_submit(): username = form.username.data percent = form.percent.data.strip() if form.percent.data else None @@ -2700,7 +2702,7 @@ def api_modify_acls(repo, namespace=None, username=None): project = _get_repo(repo, username, namespace) _check_token(project, project_token=False) - form = pagure.forms.ModifyACLForm(meta={'csrf': False}) + form = pagure.forms.ModifyACLForm(meta={"csrf": False}) if form.validate_on_submit(): acl = form.acl.data group = None @@ -3189,7 +3191,9 @@ def api_project_create_api_token(repo, namespace=None, username=None): ) authorized_acls = pagure_config.get("USER_ACLS", []) - form = pagure.forms.NewTokenForm(meta={'csrf': False}, sacls=authorized_acls) + form = pagure.forms.NewTokenForm( + meta={"csrf": False}, sacls=authorized_acls + ) if form.validate_on_submit(): acls = form.acls.data description = form.description.data diff --git a/pagure/hooks/files/git_multimail_upstream.py b/pagure/hooks/files/git_multimail_upstream.py index 7983052..dad85d0 100755 --- a/pagure/hooks/files/git_multimail_upstream.py +++ b/pagure/hooks/files/git_multimail_upstream.py @@ -114,7 +114,6 @@ if PYTHON3: except UnicodeEncodeError: return out.decode(ENCODING) - else: def is_string(s): diff --git a/pagure/internal/__init__.py b/pagure/internal/__init__.py index 4c3680d..ca29633 100644 --- a/pagure/internal/__init__.py +++ b/pagure/internal/__init__.py @@ -226,7 +226,7 @@ def check_ssh_access(): @internal_access_only def pull_request_add_comment(): """Add a comment to a pull-request.""" - pform = pagure.forms.ProjectCommentForm(meta={'csrf': False}) + pform = pagure.forms.ProjectCommentForm(meta={"csrf": False}) if not pform.validate_on_submit(): flask.abort(400, description="Invalid request") @@ -240,7 +240,7 @@ def pull_request_add_comment(): if not request: flask.abort(404, description="Pull-request not found") - form = pagure.forms.AddPullRequestCommentForm(meta={'csrf': False}) + form = pagure.forms.AddPullRequestCommentForm(meta={"csrf": False}) if not form.validate_on_submit(): flask.abort(400, description="Invalid request") @@ -277,7 +277,7 @@ def pull_request_add_comment(): @internal_access_only def ticket_add_comment(): """Add a comment to an issue.""" - pform = pagure.forms.ProjectCommentForm(meta={'csrf': False}) + pform = pagure.forms.ProjectCommentForm(meta={"csrf": False}) if not pform.validate_on_submit(): flask.abort(400, description="Invalid request") @@ -308,7 +308,7 @@ def ticket_add_comment(): "to view it", ) - form = pagure.forms.CommentForm(meta={'csrf': False}) + form = pagure.forms.CommentForm(meta={"csrf": False}) if not form.validate_on_submit(): flask.abort(400, description="Invalid request") diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index 05a0ed0..e3a5b30 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -932,7 +932,7 @@ def update_checksums_file(self, session, folder, filenames): # for each files computes the different algorythm supported with open(os.path.join(folder, filename), "rb") as stream: while True: - buf = stream.read(2 * 2 ** 10) + buf = stream.read(2 * 2**10) if buf: for hasher in algos.values(): hasher.update(buf) diff --git a/pagure/ui/fork.py b/pagure/ui/fork.py index 3125d0f..01556e1 100644 --- a/pagure/ui/fork.py +++ b/pagure/ui/fork.py @@ -557,8 +557,15 @@ def request_pull_edit(repo, requestid, username=None, namespace=None): form = pagure.forms.RequestPullEditForm(branches=flask.g.branches) if form.validate_on_submit(): request.title = form.title.data.strip() if form.title.data else None - request.initial_comment = form.initial_comment.data.strip() if form.initial_comment.data else None - request.branch = form.branch_to.data.strip() if form.branch_to.data else None + request.initial_comment = ( + form.initial_comment.data.strip() + if form.initial_comment.data + else None + ) + request.branch = ( + form.branch_to.data.strip() if form.branch_to.data else None + ) + if flask.g.fas_user.username == request.user.username: request.allow_rebase = form.allow_rebase.data flask.g.session.add(request) @@ -1721,7 +1728,11 @@ def new_request_pull( if orig_commit: orig_commit = orig_commit.oid.hex - initial_comment = form.initial_comment.data.strip() if form.initial_comment.data else None + initial_comment = ( + form.initial_comment.data.strip() + if form.initial_comment.data + else None + ) commit_start = commit_stop = None if diff_commits: commit_stop = diff_commits[0].oid.hex @@ -1887,8 +1898,12 @@ def new_remote_request_pull(repo, username=None, namespace=None): except Exception as err: flask.abort(500, description=err) - branch_from = form.branch_from.data.strip() if form.branch_from.data else None - branch_to = form.branch_to.data.strip() if form.branch_to.data else None + branch_from = ( + form.branch_from.data.strip() if form.branch_from.data else None + ) + branch_to = ( + form.branch_to.data.strip() if form.branch_to.data else None + ) remote_git = form.git_repo.data.strip() if form.git_repo.data else None repopath = pagure.utils.get_remote_repo_path(remote_git, branch_from) @@ -1969,7 +1984,10 @@ def new_remote_request_pull(repo, username=None, namespace=None): user=flask.g.fas_user.username, ) - if form.initial_comment.data and form.initial_comment.data.strip() != "": + if ( + form.initial_comment.data + and form.initial_comment.data.strip() != "" + ): pagure.lib.query.add_pull_request_comment( flask.g.session, request=request, @@ -2027,7 +2045,9 @@ def new_remote_request_pull(repo, username=None, namespace=None): except pygit2.GitError: branch_to = "master" else: - branch_to = form.branch_to.data.strip() if form.branch_to.data else None + branch_to = ( + form.branch_to.data.strip() if form.branch_to.data else None + ) return flask.render_template( "remote_pull_request.html", diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py index 4e9853c..3a3f31e 100644 --- a/pagure/ui/issues.py +++ b/pagure/ui/issues.py @@ -225,7 +225,11 @@ def update_issue(repo, issueid, username=None, namespace=None): new_milestone = None try: if repo.milestones: - new_milestone = form.milestone.data.strip() if form.milestone.data else None + new_milestone = ( + form.milestone.data.strip() + if form.milestone.data + else None + ) except AttributeError: pass diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index e0a9ca2..b5a7c2b 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -2374,7 +2374,9 @@ def add_token(repo, username=None, namespace=None): 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, From 349fe5a9a2eeb103268dd61dbe34395eba98dd4e Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 4/27] test: minor typo in multiple asserts corrected tests failed because of a missing trailing dot in the assert statement. 'Not a valid choice' > 'Not a valid choice.' 'YYYY-MM, YYYY/MM, YYYY.MM, YYYY, W' > 'YYYY-MM, YYYY/MM, YYYY.MM, YYYY, W.' --- diff --git a/tests/test_pagure_flask_api_issue.py b/tests/test_pagure_flask_api_issue.py index fc4a056..249de12 100644 --- a/tests/test_pagure_flask_api_issue.py +++ b/tests/test_pagure_flask_api_issue.py @@ -671,7 +671,7 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"milestone": ["Not a valid choice"]}, + "errors": {"milestone": ["Not a valid choice."]}, }, ) @@ -3159,7 +3159,7 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"milestone": ["Not a valid choice"]}, + "errors": {"milestone": ["Not a valid choice."]}, }, ) diff --git a/tests/test_pagure_flask_api_issue_change_status.py b/tests/test_pagure_flask_api_issue_change_status.py index 0723fdd..f31dd7d 100644 --- a/tests/test_pagure_flask_api_issue_change_status.py +++ b/tests/test_pagure_flask_api_issue_change_status.py @@ -159,7 +159,7 @@ class PagureFlaskApiIssueChangeStatustests(tests.Modeltests): { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice"]}, + "errors": {"status": ["Not a valid choice."]}, }, ) diff --git a/tests/test_pagure_flask_api_pr_flag.py b/tests/test_pagure_flask_api_pr_flag.py index 2d73e16..34eb011 100644 --- a/tests/test_pagure_flask_api_pr_flag.py +++ b/tests/test_pagure_flask_api_pr_flag.py @@ -902,7 +902,7 @@ class PagureFlaskApiPRFlagtests(tests.Modeltests): { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice"]}, + "errors": {"status": ["Not a valid choice."]}, }, ) @@ -1059,7 +1059,7 @@ class PagureFlaskApiPRFlagUserTokentests(tests.Modeltests): { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice"]}, + "errors": {"status": ["Not a valid choice."]}, }, ) diff --git a/tests/test_pagure_flask_api_project.py b/tests/test_pagure_flask_api_project.py index 8331ce6..3cdaa47 100644 --- a/tests/test_pagure_flask_api_project.py +++ b/tests/test_pagure_flask_api_project.py @@ -2780,7 +2780,7 @@ class PagureFlaskApiProjectFlagtests(tests.Modeltests): expected_output = { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice"]}, + "errors": {"status": ["Not a valid choice."]}, } if self.get_wtforms_version() >= (2, 3): expected_output["errors"]["status"] = ["This field is required."] @@ -2919,7 +2919,7 @@ class PagureFlaskApiProjectFlagtests(tests.Modeltests): self.assertEqual( data, { - "errors": {"status": ["Not a valid choice"]}, + "errors": {"status": ["Not a valid choice."]}, "error_code": "EINVALIDREQ", "error": "Invalid or incomplete input submitted", }, @@ -3371,7 +3371,7 @@ class PagureFlaskApiProjectFlagtests(tests.Modeltests): self.assertEqual( data, { - "errors": {"status": ["Not a valid choice"]}, + "errors": {"status": ["Not a valid choice."]}, "error_code": "EINVALIDREQ", "error": "Invalid or incomplete input submitted", }, @@ -3582,7 +3582,7 @@ class PagureFlaskApiProjectModifyAclTests(tests.Modeltests): "error_code": "EINVALIDREQ", "errors": { "name": ["This field is required."], - "user_type": ["Not a valid choice"], + "user_type": ["Not a valid choice."], }, } if self.get_wtforms_version() >= (2, 3): @@ -3605,7 +3605,7 @@ class PagureFlaskApiProjectModifyAclTests(tests.Modeltests): expected_output = { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"acl": ["Not a valid choice"]}, + "errors": {"acl": ["Not a valid choice."]}, } self.assertEqual(data, expected_output) @@ -5669,7 +5669,7 @@ class PagureFlaskApiProjectCreateProjectTests(tests.Modeltests): { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"namespace": ["Not a valid choice"]}, + "errors": {"namespace": ["Not a valid choice."]}, }, ) diff --git a/tests/test_pagure_flask_api_ui_private_repo.py b/tests/test_pagure_flask_api_ui_private_repo.py index f0aba34..a11d1c1 100644 --- a/tests/test_pagure_flask_api_ui_private_repo.py +++ b/tests/test_pagure_flask_api_ui_private_repo.py @@ -3244,7 +3244,7 @@ class PagurePrivateRepotest(tests.Modeltests): { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice"]}, + "errors": {"status": ["Not a valid choice."]}, }, ) diff --git a/tests/test_pagure_flask_api_user.py b/tests/test_pagure_flask_api_user.py index 495f482..04a0e6c 100644 --- a/tests/test_pagure_flask_api_user.py +++ b/tests/test_pagure_flask_api_user.py @@ -378,7 +378,7 @@ class PagureFlaskApiUSertests(tests.Modeltests): "error": "Could not match input '2016asd' to any of the following formats: " "YYYY-MM-DD, YYYY-M-DD, YYYY-M-D, YYYY/MM/DD, YYYY/M/DD, YYYY/M/D, " "YYYY.MM.DD, YYYY.M.DD, YYYY.M.D, YYYYMMDD, YYYY-DDDD, YYYYDDDD, " - "YYYY-MM, YYYY/MM, YYYY.MM, YYYY, W", + "YYYY-MM, YYYY/MM, YYYY.MM, YYYY, W.", "error_code": "ENOCODE", } self.assertEqual(json.loads(output.get_data(as_text=True)), exp) From e830f17a0c86ac46b581dc2b7ce5141c44eef75d Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 5/27] fix: var 'message' = type 'str' in 'new_git_tag()' If 'message' parameter is provided, pygit2 requires the value to be a string. Former default to 'None' caused two tests to fail with a 'TypeError'. Adjusted to enforce that empty string get passed to pygit2 if 'message' is None. https://github.com/libgit2/pygit2/blob/master/src/repository.c#L1225 --- diff --git a/pagure/lib/git.py b/pagure/lib/git.py index 84bc914..c1dc281 100644 --- a/pagure/lib/git.py +++ b/pagure/lib/git.py @@ -2439,7 +2439,7 @@ def get_git_tags(project, with_commits=False): return tags -def new_git_tag(project, tagname, target, user, message=None, force=False): +def new_git_tag(project, tagname, target, user, message=str(), force=False): """Create a new git tag in the git repositorie of the specified project. :arg project: the project in which we want to create a git tag @@ -2471,7 +2471,7 @@ def new_git_tag(project, tagname, target, user, message=None, force=False): target, target_obj.type, pygit2.Signature(user.fullname, user.default_email), - message, + message if message else str(), ) return tag From 39b99cc579672170ad62a8de9325cf6e803c9117 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 6/27] fix: arrow 'timestamp' property removed in >=v1.0.0 The missing property in newer arrow versions, caused that variables only contained references like '>' instead the actual timestamp values. Tests in 'tests/test_pagure_flask_dump_load_ticket.py' failed with 'ValueError: could not convert string to float'. References https://arrow.readthedocs.io/en/latest/releases.html Quote: 'Calls to the timestamp property now emit a DeprecationWarning.' https://github.com/arrow-py/arrow/issues/832 Quote: 'The .timestamp property has been removed to improve compatibility with datetime. Use .int_timestamp for the same results.' https://github.com/libgit2/pygit2/issues/1122 --- diff --git a/pagure/lib/model.py b/pagure/lib/model.py index 252d17c..a672818 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -152,7 +152,10 @@ def create_default_status(session, acls=None): def arrow_ts(value): - return "%s" % arrow.get(value).timestamp + if hasattr(arrow, 'timestamp'): + return "%s" % arrow.get(value).timestamp # arrow < v1.0.0 + else: + return "%s" % arrow.get(value).int_timestamp # arrow >= v1.0.0 class AccessLevels(BASE): From 1b7917f36471c5dc50b8a6fe120d0a2e9c576fb1 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 7/27] fix: flask-wtf csrf validation requires timestamp Since v0.14 the csrf lifetime and validation in flask-wtf is based on timestamps. Test 'tests/test_pagure_flask_form.py' failed because a date was used if flask-wtf version > 0.10.0. For backward compatibility, a date is still used for flask-wtf versions >=0.10.0 and < 0.14.0 Reference: https://flask-wtf.readthedocs.io/en/latest/changes/#version-0-14 --- diff --git a/tests/test_pagure_flask_form.py b/tests/test_pagure_flask_form.py index 3613554..659f367 100644 --- a/tests/test_pagure_flask_form.py +++ b/tests/test_pagure_flask_form.py @@ -68,9 +68,14 @@ class PagureFlaskFormTests(tests.SimplePagureTest): data = form.csrf_token.current_token # CSRF token expired - if hasattr(flask_wtf, "__version__") and tuple( - [int(v) for v in flask_wtf.__version__.split(".")] - ) < (0, 10, 0): + if hasattr(flask_wtf, "__version__"): + flask_wtf_version = tuple( + [int(v) for v in flask_wtf.__version__.split(".")] + ) + if flask_wtf_version and ( + flask_wtf_version < (0, 10, 0) or + flask_wtf_version >= (0, 14, 0) + ): expires = time.time() - 1 else: expires = ( From 9831b06184c2405e401b3ad3ddb8204045d36235 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 8/27] test(ui_plugins_pagure_ci): returned html changed The tested input fields contain an additional attribut (maxlength) since wtforms version >=3.0.0 Additional variation added to keep backward compatibility with older versions. Reference: https://github.com/wtforms/wtforms/commit/16d3f546ec8927d24ebf46ea65e7c0dc79438b59 --- diff --git a/tests/test_pagure_flask_ui_plugins_pagure_ci.py b/tests/test_pagure_flask_ui_plugins_pagure_ci.py index 063bf9b..412e1db 100644 --- a/tests/test_pagure_flask_ui_plugins_pagure_ci.py +++ b/tests/test_pagure_flask_ui_plugins_pagure_ci.py @@ -223,14 +223,29 @@ class PagureFlaskPluginPagureCItests(tests.SimplePagureTest): ) self.assertNotIn("Hook Pagure CI activated", output_text) - if self.get_wtforms_version() >= (2, 2): + if self.get_wtforms_version() >= (3, 0): self.assertIn( '
\n ' - '\n
\n \n ' + '
This field is required.\n ' + '\n
\n \n ' '
This field is required.
', output_text, ) + elif self.get_wtforms_version() >= (2, 2): + self.assertIn( + '
\n ' + '\n
\n \n ' + '
This field is required.\n ' ' Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 9/27] test(lib_git_auth): warnings changed output Tested string not found because of 'SAWarning: relationship' messages in the output. They are caused by changes in SQLAlchemy, as workaround until those warnings are fixed: Test statement reduced to the part that still can be identified. --- diff --git a/tests/test_pagure_lib_git_auth.py b/tests/test_pagure_lib_git_auth.py index cc1bfa8..b75e34e 100644 --- a/tests/test_pagure_lib_git_auth.py +++ b/tests/test_pagure_lib_git_auth.py @@ -113,7 +113,6 @@ class PagureLibGitAuthtests(tests.Modeltests): self.assertEqual(output.status_code, 200) output_text = output.get_data(as_text=True) self.assertIn( - "Remote hook declined the push: " "Denied push for ref 'refs/heads/master' for user 'pingou'", output_text, ) @@ -156,7 +155,6 @@ class PagureLibGitAuthtests(tests.Modeltests): self.assertEqual(output.status_code, 200) output_text = output.get_data(as_text=True) self.assertIn( - "Remote hook declined the push: " "Denied push for ref 'refs/heads/master' for user 'pingou'", output_text, ) From 72934b4f901d976bbaac894e44d1fd07f443ec3c Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 10/27] style: flake8 and black findings corrected --- diff --git a/pagure/lib/model.py b/pagure/lib/model.py index a672818..62428ee 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -152,10 +152,10 @@ def create_default_status(session, acls=None): def arrow_ts(value): - if hasattr(arrow, 'timestamp'): - return "%s" % arrow.get(value).timestamp # arrow < v1.0.0 + if hasattr(arrow, "timestamp"): + return "%s" % arrow.get(value).timestamp # arrow < v1.0.0 else: - return "%s" % arrow.get(value).int_timestamp # arrow >= v1.0.0 + return "%s" % arrow.get(value).int_timestamp # arrow >= v1.0.0 class AccessLevels(BASE): diff --git a/tests/test_pagure_flask_form.py b/tests/test_pagure_flask_form.py index 659f367..d931bbd 100644 --- a/tests/test_pagure_flask_form.py +++ b/tests/test_pagure_flask_form.py @@ -73,8 +73,8 @@ class PagureFlaskFormTests(tests.SimplePagureTest): [int(v) for v in flask_wtf.__version__.split(".")] ) if flask_wtf_version and ( - flask_wtf_version < (0, 10, 0) or - flask_wtf_version >= (0, 14, 0) + flask_wtf_version < (0, 10, 0) + or flask_wtf_version >= (0, 14, 0) ): expires = time.time() - 1 else: From 25091a3c33b99d744bf2e6695fea32b660838f7c Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 11/27] tests: wtforms changed error messages In wtforms version >= 2.3.2 localizations where unified with ending dots. From version >=3.0.0 also the english localization was adjusted. 'Not a valid choice' became 'Not a valid choice.', logic added to assert based on version. References: https://github.com/wtforms/wtforms/issues/613 https://github.com/wtforms/wtforms/pull/620 https://github.com/wtforms/wtforms/compare/2.3.2...master https://github.com/wtforms/wtforms/commit/514a9722260aa71e7f3457924cedafeff557f18f --- diff --git a/tests/test_pagure_flask_api_issue.py b/tests/test_pagure_flask_api_issue.py index 249de12..a0a838e 100644 --- a/tests/test_pagure_flask_api_issue.py +++ b/tests/test_pagure_flask_api_issue.py @@ -666,14 +666,14 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): ) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertDictEqual( - data, - { - "error": "Invalid or incomplete input submitted", - "error_code": "EINVALIDREQ", - "errors": {"milestone": ["Not a valid choice."]}, - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"milestone": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["milestone"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) def test_api_new_issue_milestone(self): """Test the api_new_issue method of the flask api.""" @@ -3154,14 +3154,14 @@ class PagureFlaskApiIssuetests(tests.SimplePagureTest): ) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertDictEqual( - data, - { - "error": "Invalid or incomplete input submitted", - "error_code": "EINVALIDREQ", - "errors": {"milestone": ["Not a valid choice."]}, - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"milestone": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["milestone"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) @patch.dict( "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True} diff --git a/tests/test_pagure_flask_api_issue_change_status.py b/tests/test_pagure_flask_api_issue_change_status.py index f31dd7d..bed74ed 100644 --- a/tests/test_pagure_flask_api_issue_change_status.py +++ b/tests/test_pagure_flask_api_issue_change_status.py @@ -154,14 +154,14 @@ class PagureFlaskApiIssueChangeStatustests(tests.Modeltests): ) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertDictEqual( - data, - { - "error": "Invalid or incomplete input submitted", - "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice."]}, - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"status": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["status"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) # No change repo = pagure.lib.query.get_authorized_project(self.session, "test") diff --git a/tests/test_pagure_flask_api_pr_flag.py b/tests/test_pagure_flask_api_pr_flag.py index 34eb011..687e4d3 100644 --- a/tests/test_pagure_flask_api_pr_flag.py +++ b/tests/test_pagure_flask_api_pr_flag.py @@ -897,14 +897,14 @@ class PagureFlaskApiPRFlagtests(tests.Modeltests): ) data = json.loads(output.get_data(as_text=True)) self.assertEqual(output.status_code, 400) - self.assertDictEqual( - data, - { - "error": "Invalid or incomplete input submitted", - "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice."]}, - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"status": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["status"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) class PagureFlaskApiPRFlagUserTokentests(tests.Modeltests): @@ -1054,14 +1054,14 @@ class PagureFlaskApiPRFlagUserTokentests(tests.Modeltests): ) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertDictEqual( - data, - { - "error": "Invalid or incomplete input submitted", - "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice."]}, - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"status": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["status"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) # No change self.session.commit() diff --git a/tests/test_pagure_flask_api_project.py b/tests/test_pagure_flask_api_project.py index 3cdaa47..ff8ad2c 100644 --- a/tests/test_pagure_flask_api_project.py +++ b/tests/test_pagure_flask_api_project.py @@ -2780,7 +2780,7 @@ class PagureFlaskApiProjectFlagtests(tests.Modeltests): expected_output = { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice."]}, + "errors": {"status": ["Not a valid choice"]}, } if self.get_wtforms_version() >= (2, 3): expected_output["errors"]["status"] = ["This field is required."] @@ -2916,14 +2916,14 @@ class PagureFlaskApiProjectFlagtests(tests.Modeltests): ) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertEqual( - data, - { - "errors": {"status": ["Not a valid choice."]}, - "error_code": "EINVALIDREQ", - "error": "Invalid or incomplete input submitted", - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"status": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["status"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) def test_flag_commit_with_uid(self): """Test flagging a commit with provided uid.""" @@ -3368,14 +3368,14 @@ class PagureFlaskApiProjectFlagtests(tests.Modeltests): ) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertEqual( - data, - { - "errors": {"status": ["Not a valid choice."]}, - "error_code": "EINVALIDREQ", - "error": "Invalid or incomplete input submitted", - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"status": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["status"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) def test_commit_flags(self): """Test retrieving commit flags.""" @@ -3605,9 +3605,11 @@ class PagureFlaskApiProjectModifyAclTests(tests.Modeltests): expected_output = { "error": "Invalid or incomplete input submitted", "error_code": "EINVALIDREQ", - "errors": {"acl": ["Not a valid choice."]}, + "errors": {"acl": ["Not a valid choice"]}, } - self.assertEqual(data, expected_output) + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["acl"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) def test_api_modify_acls_user(self): """Test the api_modify_acls method of the flask api for @@ -5664,14 +5666,14 @@ class PagureFlaskApiProjectCreateProjectTests(tests.Modeltests): output = self.app.post("/api/0/new/", data=data, headers=headers) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertDictEqual( - data, - { - "error": "Invalid or incomplete input submitted", - "error_code": "EINVALIDREQ", - "errors": {"namespace": ["Not a valid choice."]}, - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"namespace": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["namespace"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) data = { "name": "test_42", diff --git a/tests/test_pagure_flask_api_ui_private_repo.py b/tests/test_pagure_flask_api_ui_private_repo.py index a11d1c1..fb652f7 100644 --- a/tests/test_pagure_flask_api_ui_private_repo.py +++ b/tests/test_pagure_flask_api_ui_private_repo.py @@ -3239,14 +3239,14 @@ class PagurePrivateRepotest(tests.Modeltests): ) self.assertEqual(output.status_code, 400) data = json.loads(output.get_data(as_text=True)) - self.assertDictEqual( - data, - { - "error": "Invalid or incomplete input submitted", - "error_code": "EINVALIDREQ", - "errors": {"status": ["Not a valid choice."]}, - }, - ) + expected_output = { + "error": "Invalid or incomplete input submitted", + "error_code": "EINVALIDREQ", + "errors": {"status": ["Not a valid choice"]}, + } + if self.get_wtforms_version() >= (3, 0): + expected_output["errors"]["status"] = ["Not a valid choice."] + self.assertDictEqual(data, expected_output) # No change repo = pagure.lib.query._get_project(self.session, "test4") From 5eb40d6138f46a1d73f774d780a7809f23d7d50c Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 12/27] style: set 'black' target version = py39 'black' behave differently depending on the used version, especially if no python target version is set. To avoid failed style tests caused by auto discovery, target version was set to py39 and files adjusted accordingly. Two files required individual excludes ('# fmt: skip' and '# fmt: off|on') to get them passing the tests with different 'black' versions. --- diff --git a/pagure/hooks/files/git_multimail_upstream.py b/pagure/hooks/files/git_multimail_upstream.py index dad85d0..346c2a8 100755 --- a/pagure/hooks/files/git_multimail_upstream.py +++ b/pagure/hooks/files/git_multimail_upstream.py @@ -84,6 +84,7 @@ def is_ascii(s): return all(ord(c) < 128 and ord(c) > 0 for c in s) +# fmt: off if PYTHON3: def is_string(s): @@ -114,7 +115,9 @@ if PYTHON3: except UnicodeEncodeError: return out.decode(ENCODING) + else: +# fmt: on def is_string(s): try: @@ -471,7 +474,7 @@ def read_output(cmd, input=None, keepends=False, **kw): stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - **kw + **kw, ) (out, err) = p.communicate(input) out = bytes_to_str(out, errors=errors) @@ -2939,7 +2942,7 @@ class FilterLinesEnvironmentMixin(Environment): strict_utf8=True, email_max_line_length=500, max_subject_length=500, - **kw + **kw, ): super(FilterLinesEnvironmentMixin, self).__init__(**kw) self.__strict_utf8 = strict_utf8 @@ -3076,7 +3079,7 @@ class StaticRecipientsEnvironmentMixin(Environment): announce_recipients, revision_recipients, scancommitforcc, - **kw + **kw, ): super(StaticRecipientsEnvironmentMixin, self).__init__(**kw) @@ -3171,7 +3174,7 @@ class ConfigRecipientsEnvironmentMixin( config, "commitlist", "mailinglist" ), scancommitforcc=config.get("scancommitforcc"), - **kw + **kw, ) def _get_recipients(self, config, *names): @@ -3206,7 +3209,7 @@ class StaticRefFilterEnvironmentMixin(Environment): ref_filter_excl_regex, ref_filter_do_send_regex, ref_filter_dont_send_regex, - **kw + **kw, ): super(StaticRefFilterEnvironmentMixin, self).__init__(**kw) @@ -3293,7 +3296,7 @@ class ConfigRefFilterEnvironmentMixin( ref_filter_dont_send_regex=self._get_regex( config, "refFilterDontSendRegex" ), - **kw + **kw, ) diff --git a/pagure/lib/git.py b/pagure/lib/git.py index c1dc281..293d172 100644 --- a/pagure/lib/git.py +++ b/pagure/lib/git.py @@ -1354,7 +1354,7 @@ def read_output(cmd, abspath, input=None, keepends=False, error=False, **kw): stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=abspath, - **kw + **kw, ) (out, err) = procs.communicate(input) retcode = procs.wait() @@ -1386,7 +1386,7 @@ def read_git_output( input=input, keepends=keepends, error=error, - **kw + **kw, ) diff --git a/pagure/lib/git_auth.py b/pagure/lib/git_auth.py index c4c0892..36f7c7a 100644 --- a/pagure/lib/git_auth.py +++ b/pagure/lib/git_auth.py @@ -880,7 +880,7 @@ class PagureGitAuth(GitAuthHelper): pull_request, repotype, is_internal, - **info + **info, ): if is_internal: self.info("Internal push allowed") diff --git a/pagure/lib/query.py b/pagure/lib/query.py index 97ff6e7..3937e06 100644 --- a/pagure/lib/query.py +++ b/pagure/lib/query.py @@ -5524,7 +5524,7 @@ def get_pagination_metadata( flask_request.endpoint, per_page=per_page, _external=True, - **request_args_wo_page + **request_args_wo_page, ) prev_page = None @@ -5534,7 +5534,7 @@ def get_pagination_metadata( flask_request.endpoint, per_page=per_page, _external=True, - **request_args_wo_page + **request_args_wo_page, ) request_args_wo_page.update({key_page: 1}) @@ -5542,7 +5542,7 @@ def get_pagination_metadata( flask_request.endpoint, per_page=per_page, _external=True, - **request_args_wo_page + **request_args_wo_page, ) request_args_wo_page.update({key_page: pages}) @@ -5550,7 +5550,7 @@ def get_pagination_metadata( flask_request.endpoint, per_page=per_page, _external=True, - **request_args_wo_page + **request_args_wo_page, ) return { diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index e3a5b30..59000ad 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -932,7 +932,7 @@ def update_checksums_file(self, session, folder, filenames): # for each files computes the different algorythm supported with open(os.path.join(folder, filename), "rb") as stream: while True: - buf = stream.read(2 * 2**10) + buf = stream.read(2 * 2**10) # fmt: skip if buf: for hasher in algos.values(): hasher.update(buf) diff --git a/pagure/ui/clone.py b/pagure/ui/clone.py index 59a225e..ab4fbbc 100644 --- a/pagure/ui/clone.py +++ b/pagure/ui/clone.py @@ -267,7 +267,7 @@ def proxy_repospanner(project, service): "x-Extra-project_user": project.user if project.is_fork else "", "X-Extra-project_namespace": project.namespace, }, - **streamargs + **streamargs, ) # Strip out any headers that cause problems diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py index 3a3f31e..1f90e30 100644 --- a/pagure/ui/issues.py +++ b/pagure/ui/issues.py @@ -634,7 +634,7 @@ def view_issues(repo, username=None, namespace=None): no_milestones=no_stone, count=True, status=status, - **fields + **fields, ) else: if status.lower() in ["open", "true"]: @@ -658,7 +658,7 @@ def view_issues(repo, username=None, namespace=None): order=order, order_key=order_key, status=status, - **fields + **fields, ) issues_cnt = pagure.lib.query.search_issues( flask.g.session, @@ -669,7 +669,7 @@ def view_issues(repo, username=None, namespace=None): no_milestones=no_stone, count=True, status=status_count, - **fields + **fields, ) oth_issues_cnt = pagure.lib.query.search_issues( flask.g.session, @@ -680,7 +680,7 @@ def view_issues(repo, username=None, namespace=None): no_milestones=no_stone, count=True, status=other_status_count, - **fields + **fields, ) else: issues = pagure.lib.query.search_issues( @@ -693,7 +693,7 @@ def view_issues(repo, username=None, namespace=None): custom_search=custom_search, order=order, order_key=order_key, - **fields + **fields, ) issues_cnt = pagure.lib.query.search_issues( flask.g.session, @@ -702,7 +702,7 @@ def view_issues(repo, username=None, namespace=None): search_pattern=search_pattern, custom_search=custom_search, count=True, - **fields + **fields, ) oth_issues_cnt = pagure.lib.query.search_issues( flask.g.session, @@ -713,7 +713,7 @@ def view_issues(repo, username=None, namespace=None): no_milestones=no_stone, count=True, status="Open", - **fields + **fields, ) tag_list = pagure.lib.query.get_tags_of_project(flask.g.session, repo) @@ -745,7 +745,7 @@ def view_issues(repo, username=None, namespace=None): total_open=total_open, total_closed=total_closed, no_milestones=no_stone, - **fields + **fields, ) diff --git a/pyproject.toml b/pyproject.toml index f9423e3..5b06542 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [tool.black] line-length = 79 +target-version = ['py39'] include = '\.py?$' exclude = ''' /( diff --git a/tests/test_style.py b/tests/test_style.py index 91b375a..b9e780e 100644 --- a/tests/test_style.py +++ b/tests/test_style.py @@ -102,6 +102,8 @@ class TestStyle(unittest.TestCase): "--diff", "--exclude", '"/(.eggs|.git|.hg|.mypy_cache|.nox|.tox|.venv|_build|buck-out|build|dist)/"', + "--target-version", + "py39", REPO_PATH, TESTS_PATH, ] From 186c710b27aca7014b4b58b12c3199b37d7db21c Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 13/27] test(flask_api_user): return value changed format arrow version >=1.0.0 added a trailing dot to the list of supported formats, test adjusted to cover the new and old values. https://github.com/arrow-py/arrow/commit/1278a21b46fcf145592f15b47a8169f97f54415a#diff-ce633cbc3f88195da12ddbc836b59bd991035e2f9fca41f3420d5e79206af832L552-R551 --- diff --git a/tests/test_pagure_flask_api_user.py b/tests/test_pagure_flask_api_user.py index 04a0e6c..d39d7d1 100644 --- a/tests/test_pagure_flask_api_user.py +++ b/tests/test_pagure_flask_api_user.py @@ -372,7 +372,7 @@ class PagureFlaskApiUSertests(tests.Modeltests): # previous versions parsed it output = self.app.get("/api/0/user/pingou/activity/2016asd") arrow_version = self.get_arrow_version() - if arrow_version >= (0, 15, 6): + if arrow_version >= (1, 0, 0): self.assertEqual(output.status_code, 400) exp = { "error": "Could not match input '2016asd' to any of the following formats: " @@ -388,7 +388,7 @@ class PagureFlaskApiUSertests(tests.Modeltests): "error": "Could not match input '2016asd' to any of the following formats: " "YYYY-MM-DD, YYYY-M-DD, YYYY-M-D, YYYY/MM/DD, YYYY/M/DD, YYYY/M/D, " "YYYY.MM.DD, YYYY.M.DD, YYYY.M.D, YYYYMMDD, YYYY-DDDD, YYYYDDDD, " - "YYYY-MM, YYYY/MM, YYYY.MM, YYYY", + "YYYY-MM, YYYY/MM, YYYY.MM, YYYY, W", "error_code": "ENOCODE", } self.assertEqual(json.loads(output.get_data(as_text=True)), exp) From febbf04ec6bed76fc09b4691cfbe44a10ffba33f Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 14/27] fix: 'int_timestamp' replaced with 'float_timestamp' 'float_timestamp' already used in the pagure codebase and available in earlier arrow versions as 'int_timestamp' --- diff --git a/pagure/lib/model.py b/pagure/lib/model.py index 62428ee..7045f5e 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -155,7 +155,7 @@ def arrow_ts(value): if hasattr(arrow, "timestamp"): return "%s" % arrow.get(value).timestamp # arrow < v1.0.0 else: - return "%s" % arrow.get(value).int_timestamp # arrow >= v1.0.0 + return "%s" % arrow.get(value).float_timestamp # arrow >= v1.0.0 class AccessLevels(BASE): From 621fe2b9e98e3cceb94e04d1c3174cc03592f273 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 15/27] test(api_project): remove trailing dot from assert required to stay backward compatible with older versions, tests on centos-stream8 py36 fail otherwise. --- diff --git a/tests/test_pagure_flask_api_project.py b/tests/test_pagure_flask_api_project.py index ff8ad2c..2227dc6 100644 --- a/tests/test_pagure_flask_api_project.py +++ b/tests/test_pagure_flask_api_project.py @@ -3582,7 +3582,7 @@ class PagureFlaskApiProjectModifyAclTests(tests.Modeltests): "error_code": "EINVALIDREQ", "errors": { "name": ["This field is required."], - "user_type": ["Not a valid choice."], + "user_type": ["Not a valid choice"], }, } if self.get_wtforms_version() >= (2, 3): From eeb9acdc868d2b02df1c5659422c853f2a5d2609 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 16/27] fix: 'UniversalDetector' no attr '_mCharSetProbers' Attribute '_mCharSetProbers' removed from 'UniversalDetector' in 'cchardet' v3.0.0, replaced by '_charset_probers'. Version check adjusted to cover also version 5 in addition to 3 and 4. References: https://github.com/chardet/chardet/commit/be09612a5779a51695c5a69b7b73fd1b4ba12a3e https://github.com/chardet/chardet/pull/42 --- diff --git a/pagure/lib/encoding_utils.py b/pagure/lib/encoding_utils.py index 95d84e0..3412738 100644 --- a/pagure/lib/encoding_utils.py +++ b/pagure/lib/encoding_utils.py @@ -68,7 +68,7 @@ def detect_encodings(data): if cchardet: return encodings - if ch_version[0] in ("3", "4"): + if ch_version[0] in ("3", "4", "5"): for prober in detector._charset_probers: if hasattr(prober, "probers"): for prober in prober.probers: From 7235ae9ab0adcbe9def07fe4b6397a8edebb5393 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 17/27] fix: invalid json request > error 400 html response Since Flask v2.1.0, a invalid or failed json request will raise 'BadRequest' and ignore 'pagure.exceptions.APIError'. The response is therefore HTML instead a customized JSON, cause 'json_loads' to raise 'JSONDecodeError' and related tests to fail. Class 'AnyJsonRequest' added, overloads 'app.request_class' and enforce similar behavior as in previous versions. References: https://github.com/pallets/flask/issues/4552#issuecomment-1109785314 https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request.get_json "If the mimetype does not indicate JSON (application/json, see is_json), or parsing fails, on_json_loading_failed() is called and its return value is used as the return value. By default this raises a 400 Bad Request error." --- diff --git a/pagure/flask_app.py b/pagure/flask_app.py index ecbe0f8..cefa434 100644 --- a/pagure/flask_app.py +++ b/pagure/flask_app.py @@ -19,6 +19,7 @@ import time import warnings import flask +from flask.wrappers import Request import pygit2 from six.moves.urllib.parse import urljoin from whitenoise import WhiteNoise @@ -60,11 +61,22 @@ if pagure_config.get("PAGURE_CI_SERVICES"): pagure.lib.query.set_pagure_ci(pagure_config["PAGURE_CI_SERVICES"]) +# Enforce behavior of 'get_json' to return json even if +# Content-Type application/json is missing as in werkzeug < v2.1.0 +# https://github.com/pallets/flask/issues/4552#issuecomment-1109785314 +class AnyJsonRequest(Request): + def on_json_loading_failed(self, e): + if e is not None: + return super().on_json_loading_failed(e) + + def create_app(config=None): """Create the flask application.""" app = flask.Flask(__name__) app.config = pagure_config + app.request_class = AnyJsonRequest + if config: app.config.update(config) diff --git a/tests/test_pagure_flask_api_boards.py b/tests/test_pagure_flask_api_boards.py index 5c38669..dc3773d 100644 --- a/tests/test_pagure_flask_api_boards.py +++ b/tests/test_pagure_flask_api_boards.py @@ -158,7 +158,9 @@ class PagureFlaskApiBoardstests(tests.SimplePagureTest): ) def test_api_board_create_no_data(self): - headers = {"Authorization": "token aaabbbcccddd"} + headers = { + "Authorization": "token aaabbbcccddd", + } data = {} output = self.app.post( "/api/0/test/boards", headers=headers, data=data @@ -497,9 +499,6 @@ class PagureFlaskApiBoardsWithBoardtests(tests.SimplePagureTest): } # Remove the board - headers = { - "Authorization": "token aaabbbcccddd", - } data = {} output = self.app.post( "/api/0/test/boards/delete", headers=headers, data=data From c9376756f7097d66f5ea6b06a95ab00d80298b63 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 18/27] build(requirements): pin python package versions Package version pinning in all requirements files introduced. Necessary for a stable and reproducible testing environment. Also to provide clear guidance about tested package versions. Maximum package versions defined based on common ground between: rpm - F34, F36, CentOS8 Stream, pip - F34, F36 and FreeBSD 13.1. --- diff --git a/requirements-ci.txt b/requirements-ci.txt index ce1ba14..0f7f53a 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,2 +1,2 @@ -cryptography -python-jenkins +cryptography <= 36.0.0 +python-jenkins <= 1.7.0 diff --git a/requirements-ev.txt b/requirements-ev.txt index a3d98e4..e704325 100644 --- a/requirements-ev.txt +++ b/requirements-ev.txt @@ -1 +1 @@ -trololio +trololio == 1.0 diff --git a/requirements-testing.txt b/requirements-testing.txt index 6232f7c..c1145c7 100644 --- a/requirements-testing.txt +++ b/requirements-testing.txt @@ -1,18 +1,19 @@ -bcrypt -beautifulsoup4 -black -cryptography -eventlet -fedmsg -flake8 -flask-oidc -mock +bcrypt <= 3.2.2 +beautifulsoup4 <= 4.11.1 +black <= 22.8.0 +cryptography <= 36.0.0 +eventlet <= 0.33.2 +fedmsg <= 1.1.2 +flake8 <= 4.0.1 +flask-oidc <= 1.4.0 +mock <= 4.0.3 pagure-messages >= 0.0.1 -pytest -pytest-cov -pytest-xdist -python-fedora -trololio +pytest <= 6.2.5 +pytest-cov <= 4.0.0 +pytest-xdist <= 2.5.0 + +python-fedora == 1.1.1 +trololio == 1.0 # Seems that mock doesn't list this one -funcsigs +funcsigs <= 1.0.2 diff --git a/requirements.txt b/requirements.txt index 867efa9..690ef68 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,42 +1,47 @@ # Used for when working from a virtualenv. # Use this file by running "$ pip install -r requirements.txt" -alembic -arrow -bcrypt -binaryornot -bleach -blinker -celery -chardet -cryptography -docutils +alembic <= 1.8.1 +arrow <= 1.2.3 +bcrypt <= 3.2.2 +binaryornot == 0.4.4 +bleach <= 5.0.1 +blinker <= 1.5 +celery <= 5.2.6 +chardet <= 4.0.0 +cryptography <= 36.0.0 +docutils <= 0.17.1 + +# required for backward compatibility email_validator -flask -flask-wtf -kitchen -markdown -munch -Pillow -psutil -pygit2 >= 0.26.0 -python3-openid -python-openid-cla -python-openid-teams -redis -requests + +flask <= 2.2.2 +flask-wtf <= 1.0.0 +kitchen == 1.2.6 +markdown <= 3.3.7 +munch <= 2.5.0 +Pillow <= 9.2.0 +psutil <= 5.9.2 +pygit2 >= 0.26.0, <=1.8.0 +python3-openid <= 3.2.0 +python-openid-cla == 1.2 +python-openid-teams == 1.1 +redis <= 3.5.3 +requests <= 2.28.1 +six <= 1.16.0 + +# required for backward compatibility setuptools -six -# sqlalchemy minimum 0.8 -sqlalchemy >= 0.8 -straight.plugin >= 1.5.0 -whitenoise -wtforms + +sqlalchemy >= 0.8, <=1.4.46 +straight.plugin == 1.5.0 +whitenoise <= 6.2.0 +wtforms <= 3.0.1 # Required only for the `oidc` authentication backend -# flask-oidc +# flask-oidc <= 1.4.0 # Required only if `USE_FLASK_SESSION_EXT` is set to `True` # flask-session # Required only for the `fas` and `openid` authentication backends -# python-fedora +# python-fedora == 1.1.1 From 6e1b839dc8b6141f9bfea4459f795930d44ebb78 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 19/27] build(tox): align used pkgs, remove py35 and py36 To align test environment with regular deployments, ensure same python packages / requirements files are used by tox, unsupported python version (py35, py36) removed. --- diff --git a/tox.ini b/tox.ini index 4340dc8..6f9fb1c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,21 +1,16 @@ [tox] -envlist = py{35,36,37,38,39} +envlist = py{37,38,39} skipsdist = True # If the user is missing an interpreter, don't fail skip_missing_interpreters = True - [testenv] -usedevelop = True deps = - -rrequirements-testing.txt + wheel + -rrequirements.txt -rrequirements-ev.txt - python-openid - python-openid-teams - python-openid-cla - pytest-xdist - pytest-cov - pytest + -rrequirements-ci.txt + -rrequirements-testing.txt install_command = pip install {opts} {packages} setenv = From efc12ec5f54440dc724f45eed743d678f786b540 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 01:34:44 +0000 Subject: [PATCH 20/27] test(ui_remote_pr): redirect response changed 'the' added into the redirect response in werkzeug version >= 2.1.2 Reference: https://github.com/pallets/werkzeug/commit/1094fbfc646f35c31b059201370e46794b2aaf11 --- diff --git a/tests/test_pagure_flask_ui_remote_pr.py b/tests/test_pagure_flask_ui_remote_pr.py index c779a06..1aa0fe1 100644 --- a/tests/test_pagure_flask_ui_remote_pr.py +++ b/tests/test_pagure_flask_ui_remote_pr.py @@ -20,6 +20,7 @@ import time import unittest import pygit2 +import werkzeug import wtforms from mock import patch, MagicMock from bs4 import BeautifulSoup @@ -173,9 +174,21 @@ class PagureRemotePRtests(tests.Modeltests): # Try creating a remote PR output = self.app.get("/test/diff/remote") self.assertEqual(output.status_code, 302) - self.assertIn( + expected_response = ( "You should be redirected automatically to target URL: " - '= (2, 1, 2): + expected_response = ( + "You should be redirected automatically to the target URL: " + '%s" % jinja2.escape(rst) + return "
%s
" % escape(rst) else: @@ -93,7 +97,7 @@ def convert_doc(rst_string, view_file_url=None): html_string = modify_html(html_string) - html_string = markupsafe.Markup(html_string) + html_string = Markup(html_string) return html_string @@ -111,7 +115,7 @@ def convert_readme(content, ext, view_file_url=None): safe = True elif not ext or (ext and ext in [".text", ".txt"]): safe = True - output = "
%s
" % jinja2.escape(output) + output = "
%s
" % escape(output) return output, safe @@ -124,7 +128,7 @@ def load_doc(endpoint): api_docs = modify_html(api_docs) - api_docs = markupsafe.Markup(api_docs) + api_docs = Markup(api_docs) return api_docs From 8bf0f89a2bc6abd3ac9f0f938b11129045c698f1 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 10:17:57 +0000 Subject: [PATCH 23/27] test(ui_plugins_irc): renamed to 'test_plugin_irc' --- diff --git a/tests/test_pagure_flask_ui_plugins_irc.py b/tests/test_pagure_flask_ui_plugins_irc.py index 326bc22..e9624dc 100644 --- a/tests/test_pagure_flask_ui_plugins_irc.py +++ b/tests/test_pagure_flask_ui_plugins_irc.py @@ -24,7 +24,7 @@ import tests class PagureFlaskPluginIRCtests(tests.SimplePagureTest): """Tests for pagure_hook plugin of pagure""" - def test_plugin_mail(self): + def test_plugin_irc(self): """Test the irc plugin on/off endpoint.""" tests.create_projects(self.session) From 8df91d81d56285b23e55e9b1c0285c4a8412ec9f Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 22 2023 14:10:58 +0000 Subject: [PATCH 24/27] fix: TypeError in irc, mail, noff, rtd_hook plugin Fixes 'TypeError: not all arguments converted during string formatting' which was infact 'sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) NOT NULL constraint failed', caused by changes how way 'StringField' is handling the default value in 'wtforms' >= 3.0.0. In previous versions, 'None' was changed to an empty string, now it stays 'None'. Due to this change, tests and usage of IRC, Mail, Noff and rtd_hook Plugins failed, they contain 'nullable=False' fields without a default value in there Model definition. Model of the affected plugins adjusted, 'default=""' added to all 'nullable=False' fields. The behavior stays identical as with 'wtforms' < 3.0.0, which did exactly the same. References: https://github.com/wtforms/wtforms/commit/29c60458f3809bb43fb4007cfe710f3d7f4067d4 https://github.com/wtforms/wtforms/issues/291 --- diff --git a/pagure/hooks/irc.py b/pagure/hooks/irc.py index 87155ac..3b62f99 100644 --- a/pagure/hooks/irc.py +++ b/pagure/hooks/irc.py @@ -43,9 +43,9 @@ class IrcTable(BASE): index=True, ) - server = sa.Column(sa.Text, nullable=False) - port = sa.Column(sa.Text, nullable=False) - room = sa.Column(sa.Text, nullable=False) + server = sa.Column(sa.Text, nullable=False, default="") + port = sa.Column(sa.Text, nullable=False, default="") + room = sa.Column(sa.Text, nullable=False, default="") nick = sa.Column(sa.Text, nullable=True, default=None) nick_pass = sa.Column(sa.Text, nullable=True, default=None) active = sa.Column(sa.Boolean, nullable=False, default=False) diff --git a/pagure/hooks/mail.py b/pagure/hooks/mail.py index 64bcf42..5e215e7 100644 --- a/pagure/hooks/mail.py +++ b/pagure/hooks/mail.py @@ -46,7 +46,7 @@ class MailTable(BASE): index=True, ) - mail_to = sa.Column(sa.Text, nullable=False) + mail_to = sa.Column(sa.Text, nullable=False, default="") active = sa.Column(sa.Boolean, nullable=False, default=False) project = relation( diff --git a/pagure/hooks/pagure_force_commit.py b/pagure/hooks/pagure_force_commit.py index 47d4bcf..a8a818e 100644 --- a/pagure/hooks/pagure_force_commit.py +++ b/pagure/hooks/pagure_force_commit.py @@ -42,7 +42,7 @@ class PagureForceCommitTable(BASE): index=True, ) - branches = sa.Column(sa.Text, nullable=False) + branches = sa.Column(sa.Text, nullable=False, default="") active = sa.Column(sa.Boolean, nullable=False, default=False) diff --git a/pagure/hooks/rtd.py b/pagure/hooks/rtd.py index c8430ce..7946613 100644 --- a/pagure/hooks/rtd.py +++ b/pagure/hooks/rtd.py @@ -49,8 +49,8 @@ class RtdTable(BASE): active = sa.Column(sa.Boolean, nullable=False, default=False) branches = sa.Column(sa.Text, nullable=True) - api_url = sa.Column(sa.Text, nullable=False) - api_token = sa.Column(sa.Text, nullable=False) + api_url = sa.Column(sa.Text, nullable=False, default="") + api_token = sa.Column(sa.Text, nullable=False, default="") project = relation( "Project", From 08c4b1967c652bcc318f7a76be0fe4be16d29939 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 23 2023 22:08:11 +0000 Subject: [PATCH 25/27] fix(query): filtered records part of results returned projects count wrong, filtered / excluded records like 'is_fork' still part of the results. 'subquery' ignored the filter 'projects.filter(model.Project.is_fork == False', 'as_scalar' worked as expected. Similar to https://pagure.io/pagure/c/df9b15c --- diff --git a/pagure/lib/query.py b/pagure/lib/query.py index 3937e06..ad37aaf 100644 --- a/pagure/lib/query.py +++ b/pagure/lib/query.py @@ -2806,7 +2806,7 @@ def list_users_projects( projects = projects.filter(model.Project.namespace == namespace) query = session.query(model.Project).filter( - model.Project.id.in_(projects.subquery()) + model.Project.id.in_(projects.as_scalar()) ) if sort == "latest": From d269390c917fbb2cd0a3b3cdf887a86631e8172f Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 23 2023 22:36:09 +0000 Subject: [PATCH 26/27] fix: 'NoneType' object has no attribute 'replace' Caused by changes how way 'StringField' is handling the default value in 'wtforms' >= 3.0.0. In previous versions, 'None' was changed to an empty string, now it stays 'None'. Logic added to set the affected variable to 'str()'. References: https://github.com/wtforms/wtforms/commit/29c60458f3809bb43fb4007cfe710f3d7f4067d4 https://github.com/wtforms/wtforms/issues/291 --- diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index b5a7c2b..c3c4b1d 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -2598,7 +2598,7 @@ def edit_file(repo, branchname, filename, username=None, namespace=None): branch=branchname, branchto=form.branch.data, filename=filename, - content=form.content.data, + content=form.content.data if form.content.data else str(), message="%s\n\n%s" % ( form.commit_title.data.strip(), From 0c750ed7e26a2595b16c796a2c51addc26fe6d25 Mon Sep 17 00:00:00 2001 From: Dominik Wombacher Date: Jan 23 2023 22:46:57 +0000 Subject: [PATCH 27/27] fix: returned html entities ascii instead unicode Depending on installed flask packages and dependencies, the html response could contain ascii instead unicode entities. ''' added as additional replace statement in unit test to change html entity to actual apostrophe. --- diff --git a/tests/test_pagure_flask_ui_fork.py b/tests/test_pagure_flask_ui_fork.py index a63f691..8d33263 100644 --- a/tests/test_pagure_flask_ui_fork.py +++ b/tests/test_pagure_flask_ui_fork.py @@ -6382,7 +6382,9 @@ More information self.assertIn( "

fork/foo/test is not part of fork/ralph/test2's " "family

", - output.get_data(as_text=True).replace("'", "'"), + output.get_data(as_text=True) + .replace("'", "'") + .replace("'", "'"), ) @patch("pagure.lib.notify.send_email")