Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-sphinx-issues for openSUSE:Factory checked in at 2022-04-21 15:42:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-sphinx-issues (Old) and /work/SRC/openSUSE:Factory/.python-sphinx-issues.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-sphinx-issues" Thu Apr 21 15:42:42 2022 rev:7 rq:971334 version:3.0.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-sphinx-issues/python-sphinx-issues.changes 2020-03-27 00:23:03.488188745 +0100 +++ /work/SRC/openSUSE:Factory/.python-sphinx-issues.new.1538/python-sphinx-issues.changes 2022-04-21 15:49:00.336338472 +0200 @@ -1,0 +2,19 @@ +Wed Apr 20 11:24:07 UTC 2022 - pgaj...@suse.com + +- version update to 3.0.1 + 3.0.1 (2022-01-11) + * Fix regression from 3.0.0: exception: 'in <string>' requires string as left operand, not type. + 3.0.0 (2022-01-10) + * The :commit: role now outputs with an @ prefix. + * Add configuration options for changing prefixes. + * Allow {group} to be specified within issues_uri, issues_pr_uri, issues_commit_uri, and + 2.0.0 (2022-01-01) + * Drop support for Python 2.7 and 3.5. + * Test against Python 3.8 to 3.10. + * Add :cwe: role for linking to CVEs on https://cwe.mitre.org. Thanks @hugovk for the PR. + * Add support for custom urls and separators Issue #93 + * Allow custom titles for all roles Issue #116 + * Added setting issues_default_group_project as future replacement of issues_github_path, + to reflect the now to universal nature of the extension + +------------------------------------------------------------------- Old: ---- 1.2.0.tar.gz New: ---- 3.0.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-sphinx-issues.spec ++++++ --- /var/tmp/diff_new_pack.ly96di/_old 2022-04-21 15:49:00.736338899 +0200 +++ /var/tmp/diff_new_pack.ly96di/_new 2022-04-21 15:49:00.740338904 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-sphinx-issues # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %bcond_without python2 Name: python-sphinx-issues -Version: 1.2.0 +Version: 3.0.1 Release: 0 Summary: A Sphinx extension for linking to a project's issue tracker License: MIT @@ -53,7 +53,7 @@ %python_expand %fdupes %{buildroot}%{$python_sitelib} %check -%pytest test_sphinx_issues.py +%pytest %files %{python_files} %doc README.rst ++++++ 1.2.0.tar.gz -> 3.0.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/.github/workflows/lint.yml new/sphinx-issues-3.0.1/.github/workflows/lint.yml --- old/sphinx-issues-1.2.0/.github/workflows/lint.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/.github/workflows/lint.yml 2022-01-11 17:41:14.000000000 +0100 @@ -0,0 +1,12 @@ +name: Lint + +on: [push, pull_request, workflow_dispatch] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: pre-commit/action@v2.0.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/.github/workflows/test.yml new/sphinx-issues-3.0.1/.github/workflows/test.yml --- old/sphinx-issues-1.2.0/.github/workflows/test.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/.github/workflows/test.yml 2022-01-11 17:41:14.000000000 +0100 @@ -0,0 +1,34 @@ +name: Test + +on: [push, pull_request, workflow_dispatch] + +env: + FORCE_COLOR: 1 + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.6", "3.10"] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + cache: pip + cache-dependency-path: "setup.py" + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U wheel + python -m pip install -U tox + + - name: Tox tests + run: | + tox -e py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/.pre-commit-config.yaml new/sphinx-issues-3.0.1/.pre-commit-config.yaml --- old/sphinx-issues-1.2.0/.pre-commit-config.yaml 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/.pre-commit-config.yaml 2022-01-11 17:41:14.000000000 +0100 @@ -1,15 +1,20 @@ repos: -- repo: https://github.com/ambv/black - rev: 18.9b0 +- repo: https://github.com/asottile/pyupgrade + rev: v2.11.0 + hooks: + - id: pyupgrade + args: [--py36-plus] +- repo: https://github.com/python/black + rev: 20.8b1 hooks: - id: black - language_version: python3.6 -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.0.0 +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.9.0 hooks: - id: flake8 + additional_dependencies: [flake8-bugbear==21.4.3] - repo: https://github.com/asottile/blacken-docs - rev: v0.3.0 + rev: v1.10.0 hooks: - id: blacken-docs - additional_dependencies: [black==18.9b0] + additional_dependencies: [black==20.8b1] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/.travis.yml new/sphinx-issues-3.0.1/.travis.yml --- old/sphinx-issues-1.2.0/.travis.yml 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,29 +0,0 @@ -language: python -sudo: false -cache: pip -install: travis_retry pip install -U tox -script: tox -jobs: - fast_finish: true - - include: - - { python: '3.6', env: TOXENV=lint } - - { python: '2.7', env: TOXENV=py27 } - - { python: '3.5', env: TOXENV=py35 } - - { python: '3.6', env: TOXENV=py36 } - - { python: '3.7', env: TOXENV=py37, dist: xenial, sudo: true } - - - stage: PyPI Release - if: tag IS present - python: "3.6" - env: [] - install: skip - script: skip - deploy: - provider: pypi - user: sloria - on: - tags: true - distributions: sdist bdist_wheel - password: - secure: D0c2PYyI06+N5/inLaPHkEaM/GVgKVPCBDm2asmQvCTs14ory9KK17cnS+tOmrTNyMzw2tYSvD1Ar5a7MQAfcJ+p2bOnr/UCLqzt98H2LlE/2NJdzQtI3FtNCkhRVx20LK85G8ZWaHCecGIkgGmbIDZ56u1Aj+G16z0PqEz5i7s= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/LICENSE new/sphinx-issues-3.0.1/LICENSE --- old/sphinx-issues-1.2.0/LICENSE 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/LICENSE 2022-01-11 17:41:14.000000000 +0100 @@ -1,4 +1,4 @@ -Copyright 2018 Steven Loria +Copyright 2022 Steven Loria Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/README.rst new/sphinx-issues-3.0.1/README.rst --- old/sphinx-issues-1.2.0/README.rst 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/README.rst 2022-01-11 17:41:14.000000000 +0100 @@ -2,8 +2,17 @@ sphinx-issues ============= -.. image:: https://travis-ci.org/sloria/sphinx-issues.svg?branch=master - :target: https://travis-ci.org/sloria/sphinx-issues +.. image:: https://badgen.net/pypi/v/sphinx-issues + :alt: pypi badge + :target: https://pypi.org/project/sphinx-issues/ + +.. image:: https://github.com/OpenASL/HowSignBot/workflows/.github/workflows/test.yml/badge.svg + :alt: github actions status + :target: .github/workflows/test.yml + +.. image:: https://badgen.net/badge/code%20style/black/000 + :target: https://github.com/ambv/black + :alt: Code style: Black A Sphinx extension for linking to your project's issue tracker. Includes roles for linking to issues, pull requests, user profiles, with built-in support for GitHub (though this works with other services). @@ -20,7 +29,11 @@ pip install sphinx-issues -Add ``sphinx_issues`` to ``extensions`` in your ``conf.py``. If your project is on GitHub, add the ``issues_github_path`` config variable. Otherwise, use ``issues_uri`` and ``issues_pr_uri``. +Add ``sphinx_issues`` to ``extensions`` in your ``conf.py``. + +The extension has default values for GitHub projects. +Simply set the add the ``issues_default_group_project`` config variable and you are good +to go: .. code-block:: python @@ -32,16 +45,47 @@ "sphinx_issues" ] - # Github repo + # Path to GitHub repo {group}/{project} (note that `group` is the GitHub user or organization) issues_github_path = "sloria/marshmallow" - # equivalent to - issues_uri = "https://github.com/sloria/marshmallow/issues/{issue}" - issues_pr_uri = "https://github.com/sloria/marshmallow/pull/{pr}" - issues_commit_uri = "https://github.com/sloria/marshmallow/commit/{commit}" + # which is the equivalent to: + issues_uri = "https://github.com/{group}/{project}/issues/{issue}" + issues_prefix = "#" + issues_pr_uri = "https://github.com/{group}/{project}/pull/{pr}" + issues_pr_prefix = "#" + issues_commit_uri = "https://github.com/{group}/{project}/commit/{commit}" + issues_commit_prefix = "@" + issues_user_uri = "https://github.com/{user}" + issues_user_prefix = "@" + +The extension is very configurable and can be used with any kind of +issue tracker. Here is how you could configure it for use +with a custom hosed GitLab instance: + +.. code-block:: python + + # docs/conf.py + + # ... + extensions = [ + # ... + "sphinx_issues" + ] + + # Default repo {group}/{project} of gitlab project + issues_default_group_project = "myteam/super_great_project" + issues_uri = "https://gitlab.company.com/{group}/{project}/-/issues/{issue}" + issues_prefix = "#" + issues_pr_uri = "https://gitlab.company.com/{group}/{project}/-/merge_requests/{pr}" + issues_pr_prefix = "!" + issues_commit_uri = "https://gitlab.company.com/{group}/{project}/-/commit/{commit}" + issues_commit_prefix = "@" + issues_user_uri = "https://gitlab.company.com/{user}" + issues_user_prefix = "@" -Usage -***** + +Usage inside the documentation +****************************** Use the ``:issue:`` and ``:pr:`` roles in your docs like so: @@ -56,7 +100,20 @@ See PR :pr:`58` -Use the ``:user:`` role in your docs to link to user profiles (Github by default, but can be configured via the ``issues_user_uri`` config variable). +Use the ``:user:`` role in your docs to link to user profiles (GitHub by default, but can be configured via the ``issues_user_uri`` config variable). + + +Use the ``:commit:`` role to link to commits. + +.. code-block:: rst + + Fixed in :commit:`6bb9124d5e9dbb2f7b52864c3d8af7feb1b69403`. + +Use the ``:cve:`` role to link to CVEs on https://cve.mitre.org. + +.. code-block:: rst + + :cve:`CVE-2018-17175` - Addresses possible vulnerability when... .. code-block:: rst @@ -68,23 +125,24 @@ This change is due to :user:`Andreas Mueller <amueller>`. +The syntax ``:role:`My custom title <target>``` works for all roles of this extension. -Use the ``:commit:`` role to link to commits. +It can be also used in combination with a list: .. code-block:: rst - Fixed in :commit:`6bb9124d5e9dbb2f7b52864c3d8af7feb1b69403`. + Fix bad bug :issue:`123, (Duplicate) <199>` -Use the ``:cve:`` role to link to CVEs on https://cve.mitre.org. +Use the ``:cwe:`` role to link to CWEs on https://cwe.mitre.org. .. code-block:: rst - :cve:`CVE-2018-17175` - Addresses possible vulnerability when... + :cwe:`CWE-787` - The software writes data past the end, or... Credits ******* -Credit goes to Jeff Forcier for his work on the `releases <https://github.com/bitprophet/releases>`_ extension, which is a full-featured solution for generating changelogs. I just needed a quick way to reference Github issues in my docs, so I yoinked the bits that I needed. +Credit goes to Jeff Forcier for his work on the `releases <https://github.com/bitprophet/releases>`_ extension, which is a full-featured solution for generating changelogs. I just needed a quick way to reference GitHub issues in my docs, so I yoinked the bits that I needed. License ******* @@ -95,6 +153,29 @@ Changelog ********* +3.0.1 (2022-01-11) +------------------ + +- Fix regression from 3.0.0: `exception: 'in <string>' requires string as left operand, not type`. + +3.0.0 (2022-01-10) +------------------ + +- The `:commit:` role now outputs with an `@` prefix. +- Add configuration options for changing prefixes. +- Allow `{group}` to be specified within `issues_uri`, `issues_pr_uri`, `issues_commit_uri`, and + +2.0.0 (2022-01-01) +------------------ + +- Drop support for Python 2.7 and 3.5. +- Test against Python 3.8 to 3.10. +- Add ``:cwe:`` role for linking to CVEs on https://cwe.mitre.org. + Thanks @hugovk for the PR. +- Add support for custom urls and separators `Issue #93 <https://github.com/sloria/sphinx-issues/issues/93>`_ +- Allow custom titles for all roles `Issue #116 <https://github.com/sloria/sphinx-issues/issues/116>`_ +- Added setting `issues_default_group_project` as future replacement of `issues_github_path`, to reflect the now to universal nature of the extension + 1.2.0 (2018-12-26) ------------------ @@ -132,7 +213,7 @@ 0.2.0 (2014-12-22) ------------------ -- Add ``:user:`` role for linking to Github user profiles. +- Add ``:user:`` role for linking to GitHub user profiles. 0.1.0 (2014-12-21) ------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/setup.cfg new/sphinx-issues-3.0.1/setup.cfg --- old/sphinx-issues-1.2.0/setup.cfg 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/setup.cfg 2022-01-11 17:41:14.000000000 +0100 @@ -1,13 +1,9 @@ [metadata] # This includes the license file in the wheel. -license_file = LICENSE - -[bdist_wheel] -universal = 1 +license_files = LICENSE [flake8] -ignore = E203, E266, E501, W503, E302, W504 -max-line-length = 100 +ignore = E203, E266, E501, W503 +max-line-length = 90 max-complexity = 18 select = B,C,E,F,W,T4,B9 -exclude = .git,.ropeproject,.tox,build,env,venv,__pycache__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/setup.py new/sphinx-issues-3.0.1/setup.py --- old/sphinx-issues-1.2.0/setup.py 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/setup.py 2022-01-11 17:41:14.000000000 +0100 @@ -1,14 +1,13 @@ -# -*- coding: utf-8 -*- import re from setuptools import setup INSTALL_REQUIRES = ["sphinx"] EXTRAS_REQUIRE = { - "tests": ["pytest", 'mock; python_version < "3.0"'], + "tests": ["pytest>=6.2.0"], "lint": [ - "flake8==3.6.0", - 'flake8-bugbear==18.8.0; python_version >= "3.5"', - "pre-commit==1.13.0", + "flake8==3.9.2", + "flake8-bugbear==20.11.1", + "pre-commit~=2.7", ], } EXTRAS_REQUIRE["dev"] = EXTRAS_REQUIRE["tests"] + EXTRAS_REQUIRE["lint"] + ["tox"] @@ -19,7 +18,7 @@ Raises RuntimeError if not found. """ version = "" - with open(fname, "r") as fp: + with open(fname) as fp: reg = re.compile(r'__version__ = [\'"]([^\'"]*)[\'"]') for line in fp: m = reg.match(line) @@ -40,7 +39,7 @@ setup( name="sphinx-issues", version=find_version("sphinx_issues.py"), - description="A Sphinx extension for linking to your project's " "issue tracker", + description="A Sphinx extension for linking to your project's issue tracker", long_description=read("README.rst"), install_requires=INSTALL_REQUIRES, extras_require=EXTRAS_REQUIRE, @@ -49,15 +48,16 @@ url="https://github.com/sloria/sphinx-issues", license="MIT", keywords="sphinx issues github", + python_requires=">=3.6", classifiers=[ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Topic :: Software Development :: Documentation", ], py_modules=["sphinx_issues"], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/sphinx_issues.py new/sphinx-issues-3.0.1/sphinx_issues.py --- old/sphinx-issues-1.2.0/sphinx_issues.py 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/sphinx_issues.py 2022-01-11 17:41:14.000000000 +0100 @@ -1,27 +1,23 @@ -# -*- coding: utf-8 -*- """A Sphinx extension for linking to your project's issue tracker.""" import re +from typing import Callable, Optional, Tuple from docutils import nodes, utils +from sphinx.config import Config from sphinx.util.nodes import split_explicit_title -__version__ = "1.2.0" +__version__ = "3.0.1" __author__ = "Steven Loria" __license__ = "MIT" -def user_role(name, rawtext, text, lineno, inliner, options=None, content=None): - """Sphinx role for linking to a user profile. Defaults to linking to - Github profiles, but the profile URIS can be configured via the - ``issues_user_uri`` config value. +def cve_role(name, rawtext, text, lineno, inliner, options=None, content=None): + """Sphinx role for linking to a CVE on https://cve.mitre.org. Examples: :: - :user:`sloria` - - Anchor text also works: :: + :cve:`CVE-2018-17175` - :user:`Steven Loria <sloria>` """ options = options or {} content = content or [] @@ -29,26 +25,154 @@ target = utils.unescape(target).strip() title = utils.unescape(title).strip() - config = inliner.document.settings.env.app.config - if config.issues_user_uri: - ref = config.issues_user_uri.format(user=target) - else: - ref = "https://github.com/{0}".format(target) - if has_explicit_title: - text = title - else: - text = "@{0}".format(target) - + ref = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={target}" + text = title if has_explicit_title else target link = nodes.reference(text=text, refuri=ref, **options) return [link], [] -def cve_role(name, rawtext, text, lineno, inliner, options=None, content=None): - """Sphinx role for linking to a CVE on https://cve.mitre.org. +GITHUB_USER_RE = re.compile("^https://github.com/([^/]+)/([^/]+)/.*") + + +def _get_default_group_and_project( + config: Config, uri_config_option: str +) -> Optional[Tuple[str, str]]: + """ + Get the default group/project or None if not set + """ + old_config = getattr(config, "issues_github_path", None) + new_config = getattr(config, "issues_default_group_project", None) + + if old_config and new_config: + raise ValueError( + "Both 'issues_github_path' and 'issues_default_group_project' are set, even" + " though they define the same setting. " + "Please only define one of these." + ) + group_and_project = new_config or old_config + + if group_and_project: + assert isinstance(group_and_project, str) + try: + group, project = group_and_project.split("/", maxsplit=1) + return group, project + except ValueError as e: + raise ValueError( + "`issues_github_path` or `issues_default_group_project` needs to " + "define a value in the form of `<group or user>/<project>` " + f"but `{config}` was given." + ) from e + + # If group and project was not set, we need to look for it within the github url + # for backward compatibility + if not group_and_project: + uri = getattr(config, uri_config_option) + if uri: + match = GITHUB_USER_RE.match(uri) + if match: + return match.groups()[0], match.groups()[1] + + return None + + +def _get_placeholder(uri_config_option: str) -> str: + """ + Get the placeholder from the uri_config_option + """ + try: + # i.e. issues_pr_uri -> pr + return uri_config_option[:-4].split("_", maxsplit=1)[1] + except IndexError: + # issues_uri -> issue + return uri_config_option[:-5] + + +def _get_uri_template( + config: Config, + uri_config_option: str, +) -> str: + """ + Get a URL format template that can be filled with user information based + on the given configuration + + The result always contains the following placeholder + - n (the issue number, user, pull request, etc...) + + The result can contain the following other placeholders + - group (same as user in github) + - project + + Examples for possible results: + + - "https://github.com/{group}/{project}/issues/{n}" + + - "https://gitlab.company.com/{group}/{project}/{n}" + + - "https://fancy.issuetrack.com?group={group}&project={project}&issue={n}" + + Raises: + - ValueError if the given uri contains an invalid placeholder + """ + format_string = str(getattr(config, uri_config_option)) + placeholder = _get_placeholder(uri_config_option) + + result = format_string.replace(f"{{{placeholder}}}", "{n}") + + try: + result.format(project="", group="", n="") + except (NameError, KeyError) as e: + raise ValueError( + f"The `{uri_config_option}` option contains invalid placeholders. " + f"Only {{group}}, {{projects}} and {{{placeholder}}} are allowed." + f'Invalid format string: "{format_string}".' + ) from e + return result + + +def _get_uri( + uri_config_option: str, + config: Config, + number: str, + group_and_project: Optional[Tuple[str, str]] = None, +) -> str: + """ + Get a URI based on the given configuration and do some sanity checking + """ + format_string = _get_uri_template(config, uri_config_option) + + url_vars = {"n": number} + + config_group_and_project = _get_default_group_and_project(config, uri_config_option) + if group_and_project: + # Group and Project defined by call + if config_group_and_project: + to_replace = "/".join(config_group_and_project) + if to_replace in format_string: + # Backward compatibility, replace default group/project + # with {group}/{project} + format_string = format_string.replace(to_replace, "{group}/{project}") + (url_vars["group"], url_vars["project"]) = group_and_project + elif config_group_and_project: + # If not defined by call use the default if given + (url_vars["group"], url_vars["project"]) = config_group_and_project + + try: + return format_string.format(**url_vars) + except (NameError, KeyError) as e: + # The format string was checked before, that it contains no additional not + # supported placeholders. So this occur + raise ValueError( + f"The `{uri_config_option}` format `{format_string}` requires a " + f"group/project to be defined in `issues_default_group_project`." + ) from e + + +def cwe_role(name, rawtext, text, lineno, inliner, options=None, content=None): + """Sphinx role for linking to a CWE on https://cwe.mitre.org. Examples: :: - :cve:`CVE-2018-17175` + :cwe:`CWE-787` """ options = options or {} @@ -57,65 +181,80 @@ target = utils.unescape(target).strip() title = utils.unescape(title).strip() - ref = "https://cve.mitre.org/cgi-bin/cvename.cgi?name={0}".format(target) + number = target[4:] + ref = f"https://cwe.mitre.org/data/definitions/{number}.html" text = title if has_explicit_title else target link = nodes.reference(text=text, refuri=ref, **options) return [link], [] -class IssueRole(object): +class IssueRole: + # Symbols used to separate and issue/pull request/merge request etc + # i.e + # - group/project#2323 for issues + # - group/project!1234 for merge requests (in gitlab) + # - group/project@adbc1234 for commits + ELEMENT_SEPARATORS = "#@!" - EXTERNAL_REPO_REGEX = re.compile(r"^(\w+)/(.+)([#@])([\w]+)$") + EXTERNAL_REPO_REGEX = re.compile(rf"^(\w+)/(.+)([{ELEMENT_SEPARATORS}])([\w]+)$") def __init__( - self, uri_config_option, format_kwarg, github_uri_template, format_text=None + self, + config_prefix: str, + pre_format_text: Callable[[Config, str], str] = None, ): - self.uri_config_option = uri_config_option - self.format_kwarg = format_kwarg - self.github_uri_template = github_uri_template - self.format_text = format_text or self.default_format_text + self.uri_config = f"{config_prefix}_uri" + self.separator_config = f"{config_prefix}_prefix" + self.pre_format_text = pre_format_text or self.default_pre_format_text @staticmethod - def default_format_text(issue_no): - return "#{0}".format(issue_no) + def default_pre_format_text(config: Config, text: str) -> str: + return text + + def format_text(self, config: Config, issue_no: str) -> str: + """ + Add supported separator in front of the issue or raise an error if invalid + separator is given + """ + separator = getattr(config, self.separator_config) + if separator not in self.ELEMENT_SEPARATORS: + raise ValueError( + f"Option {self.separator_config} has to be one of " + f"{', '.join(self.ELEMENT_SEPARATORS)}." + ) + text = self.pre_format_text(config, issue_no.lstrip(self.ELEMENT_SEPARATORS)) + return f"{separator}{text}" + + def make_node(self, name: str, issue_no: str, config: Config, options=None): + if issue_no in ("-", "0"): + return None - def make_node(self, name, issue_no, config, options=None): - name_map = {"pr": "pull", "issue": "issues", "commit": "commit"} options = options or {} + + has_explicit_title, title, target = split_explicit_title(issue_no) + + if has_explicit_title: + issue_no = str(target) + repo_match = self.EXTERNAL_REPO_REGEX.match(issue_no) - if repo_match: # External repo - username, repo, symbol, issue = repo_match.groups() - if name not in name_map: - raise ValueError( - "External repo linking not supported for :{}:".format(name) - ) - path = name_map.get(name) - ref = "https://github.com/{issues_github_path}/{path}/{n}".format( - issues_github_path="{}/{}".format(username, repo), path=path, n=issue + + if repo_match: + # External repo + group, project, original_separator, issue_no = repo_match.groups() + text = f"{group}/{project}{self.format_text(config, issue_no)}" + ref = _get_uri( + self.uri_config, + config, + issue_no, + (group, project), ) - formatted_issue = self.format_text(issue).lstrip("#") - text = "{username}/{repo}{symbol}{formatted_issue}".format(**locals()) - link = nodes.reference(text=text, refuri=ref, **options) - return link - - if issue_no not in ("-", "0"): - uri_template = getattr(config, self.uri_config_option, None) - if uri_template: - ref = uri_template.format(**{self.format_kwarg: issue_no}) - elif config.issues_github_path: - ref = self.github_uri_template.format( - issues_github_path=config.issues_github_path, n=issue_no - ) - else: - raise ValueError( - "Neither {} nor issues_github_path " - "is set".format(self.uri_config_option) - ) - issue_text = self.format_text(issue_no) - link = nodes.reference(text=issue_text, refuri=ref, **options) else: - link = None - return link + text = self.format_text(config, issue_no) + ref = _get_uri(self.uri_config, config, issue_no) + if has_explicit_title: + return nodes.reference(text=title, refuri=ref, **options) + else: + return nodes.reference(text=text, refuri=ref, **options) def __call__( self, name, rawtext, text, lineno, inliner, options=None, content=None @@ -135,70 +274,115 @@ """Sphinx role for linking to an issue. Must have -`issues_uri` or `issues_github_path` configured in ``conf.py``. +`issues_uri` or `issues_default_group_project` configured in ``conf.py``. Examples: :: :issue:`123` :issue:`42,45` :issue:`sloria/konch#123` """ issue_role = IssueRole( - uri_config_option="issues_uri", - format_kwarg="issue", - github_uri_template="https://github.com/{issues_github_path}/issues/{n}", + config_prefix="issues", ) """Sphinx role for linking to a pull request. Must have -`issues_pr_uri` or `issues_github_path` configured in ``conf.py``. +`issues_pr_uri` or `issues_default_group_project` configured in ``conf.py``. Examples: :: :pr:`123` :pr:`42,45` :pr:`sloria/konch#43` """ pr_role = IssueRole( - uri_config_option="issues_pr_uri", - format_kwarg="pr", - github_uri_template="https://github.com/{issues_github_path}/pull/{n}", + config_prefix="issues_pr", ) -def format_commit_text(sha): +def format_commit_text(config, sha): return sha[:7] """Sphinx role for linking to a commit. Must have -`issues_pr_uri` or `issues_github_path` configured in ``conf.py``. +`issues_commit_uri` or `issues_default_group_project` configured in ``conf.py``. Examples: :: :commit:`123abc456def` :commit:`sloria/konch@123abc456def` """ commit_role = IssueRole( - uri_config_option="issues_commit_uri", - format_kwarg="commit", - github_uri_template="https://github.com/{issues_github_path}/commit/{n}", - format_text=format_commit_text, + config_prefix="issues_commit", + pre_format_text=format_commit_text, ) +"""Sphinx role for linking to a user profile. Defaults to linking to +GitHub profiles, but the profile URIS can be configured via the +``issues_user_uri`` config value. + +Examples: :: + + :user:`sloria` + +Anchor text also works: :: + + :user:`Steven Loria <sloria>` +""" +user_role = IssueRole(config_prefix="issues_user") + def setup(app): # Format template for issues URI # e.g. 'https://github.com/sloria/marshmallow/issues/{issue} - app.add_config_value("issues_uri", default=None, rebuild="html") + app.add_config_value( + "issues_uri", + default="https://github.com/{group}/{project}/issues/{issue}", + rebuild="html", + types=[str], + ) + app.add_config_value("issues_prefix", default="#", rebuild="html", types=[str]) # Format template for PR URI # e.g. 'https://github.com/sloria/marshmallow/pull/{issue} - app.add_config_value("issues_pr_uri", default=None, rebuild="html") + app.add_config_value( + "issues_pr_uri", + default="https://github.com/{group}/{project}/pull/{pr}", + rebuild="html", + types=[str], + ) + app.add_config_value("issues_pr_prefix", default="#", rebuild="html", types=[str]) # Format template for commit URI # e.g. 'https://github.com/sloria/marshmallow/commits/{commit} - app.add_config_value("issues_commit_uri", default=None, rebuild="html") - # Shortcut for Github, e.g. 'sloria/marshmallow' - app.add_config_value("issues_github_path", default=None, rebuild="html") + app.add_config_value( + "issues_commit_uri", + default="https://github.com/{group}/{project}/commit/{commit}", + rebuild="html", + types=[str], + ) + app.add_config_value( + "issues_commit_prefix", default="@", rebuild="html", types=[str] + ) + # There is no seperator config as a format_text function is given + + # Default User (Group)/Project eg. 'sloria/marshmallow' + # Called github as the package was working with github only before + app.add_config_value( + "issues_github_path", default=None, rebuild="html", types=[str] + ) + # Same as above but with new naming to reflect the new functionality + # Only on of both can be set + app.add_config_value( + "issues_default_group_project", default=None, rebuild="html", types=[str] + ) # Format template for user profile URI # e.g. 'https://github.com/{user}' - app.add_config_value("issues_user_uri", default=None, rebuild="html") + app.add_config_value( + "issues_user_uri", + default="https://github.com/{user}", + rebuild="html", + types=[str], + ) + app.add_config_value("issues_user_prefix", default="@", rebuild="html", types=[str]) app.add_role("issue", issue_role) app.add_role("pr", pr_role) app.add_role("user", user_role) app.add_role("commit", commit_role) app.add_role("cve", cve_role) + app.add_role("cwe", cwe_role) return { "version": __version__, "parallel_read_safe": True, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/test_sphinx_issues.py new/sphinx-issues-3.0.1/test_sphinx_issues.py --- old/sphinx-issues-1.2.0/test_sphinx_issues.py 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/test_sphinx_issues.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,142 +0,0 @@ -# -*- coding: utf-8 -*- -from tempfile import mkdtemp -from shutil import rmtree - -try: - from unittest.mock import Mock -except ImportError: - from mock import Mock - -from sphinx.application import Sphinx -from sphinx_issues import ( - issue_role, - user_role, - pr_role, - cve_role, - commit_role, - setup as issues_setup, -) - -import pytest - - -@pytest.yield_fixture( - params=[ - # Parametrize config - {"issues_github_path": "marshmallow-code/marshmallow"}, - { - "issues_uri": "https://github.com/marshmallow-code/marshmallow/issues/{issue}", - "issues_pr_uri": "https://github.com/marshmallow-code/marshmallow/pull/{pr}", - "issues_commit_uri": "https://github.com/marshmallow-code/marshmallow/commit/{commit}", - }, - ] -) -def app(request): - src, doctree, confdir, outdir = [mkdtemp() for _ in range(4)] - Sphinx._log = lambda self, message, wfile, nonl=False: None - app = Sphinx( - srcdir=src, confdir=None, outdir=outdir, doctreedir=doctree, buildername="html" - ) - issues_setup(app) - # Stitch together as the sphinx app init() usually does w/ real conf files - app.config._raw_config = request.param - try: - app.config.init_values() - except TypeError: - app.config.init_values(lambda x: x) - yield app - [rmtree(x) for x in (src, doctree, confdir, outdir)] - - -@pytest.fixture() -def inliner(app): - return Mock(document=Mock(settings=Mock(env=Mock(app=app)))) - - -@pytest.mark.parametrize( - ("role", "role_name", "text", "expected_text", "expected_url"), - [ - ( - issue_role, - "issue", - "42", - "#42", - "https://github.com/marshmallow-code/marshmallow/issues/42", - ), - ( - pr_role, - "pr", - "42", - "#42", - "https://github.com/marshmallow-code/marshmallow/pull/42", - ), - (user_role, "user", "sloria", "@sloria", "https://github.com/sloria"), - ( - user_role, - "user", - "Steven Loria <sloria>", - "Steven Loria", - "https://github.com/sloria", - ), - ( - cve_role, - "cve", - "CVE-2018-17175", - "CVE-2018-17175", - "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17175", - ), - ( - commit_role, - "commit", - "123abc456def", - "123abc4", - "https://github.com/marshmallow-code/marshmallow/commit/123abc456def", - ), - # External issue - ( - issue_role, - "issue", - "sloria/webargs#42", - "sloria/webargs#42", - "https://github.com/sloria/webargs/issues/42", - ), - # External PR - ( - pr_role, - "pr", - "sloria/webargs#42", - "sloria/webargs#42", - "https://github.com/sloria/webargs/pull/42", - ), - # External commit - ( - commit_role, - "commit", - "sloria/webargs@abc123def456", - "sloria/webargs@abc123d", - "https://github.com/sloria/webargs/commit/abc123def456", - ), - ], -) -def test_roles(inliner, role, role_name, text, expected_text, expected_url): - result = role(role_name, rawtext="", text=text, lineno=None, inliner=inliner) - link = result[0][0] - assert link.astext() == expected_text - assert link.attributes["refuri"] == expected_url - - -def test_issue_role_multiple(inliner): - result = issue_role( - name=None, rawtext="", text="42,43", inliner=inliner, lineno=None - ) - link1 = result[0][0] - assert link1.astext() == "#42" - issue_url = "https://github.com/marshmallow-code/marshmallow/issues/" - assert link1.attributes["refuri"] == issue_url + "42" - - sep = result[0][1] - assert sep.astext() == ", " - - link2 = result[0][2] - assert link2.astext() == "#43" - assert link2.attributes["refuri"] == issue_url + "43" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/tests/source/conf.py new/sphinx-issues-3.0.1/tests/source/conf.py --- old/sphinx-issues-1.2.0/tests/source/conf.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/tests/source/conf.py 2022-01-11 17:41:14.000000000 +0100 @@ -0,0 +1,64 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = "sphinx-issues" +copyright = "2022, foobar" +author = "foobar" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ["sphinx_issues"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "alabaster" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# +suppress_warnings = ["app.add_node"] + +issues_uri = "https://gitlab.company.com/{group}/{project}/-/issues/{issue}" +issues_prefix = "#" +issues_pr_uri = "https://gitlab.company.com/{group}/{project}/-/merge_requests/{pr}" +issues_pr_prefix = "!" +issues_commit_uri = "https://gitlab.company.com/{group}/{project}/-/commit/{commit}" +issues_commit_prefix = "@" +issues_user_uri = "https://gitlab.company.com/{user}" +issues_user_prefix = "@" +issues_default_group_project = "myteam/super_great_project" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/tests/source/examples.rst new/sphinx-issues-3.0.1/tests/source/examples.rst --- old/sphinx-issues-1.2.0/tests/source/examples.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/tests/source/examples.rst 2022-01-11 17:41:14.000000000 +0100 @@ -0,0 +1,7 @@ +Examples: + + - See issues :issue:`12,13` + + - See other issues :issue:`sloria/konch#45,46`. + + - See PR :pr:`58`, thanks :user:`kound` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/tests/source/index.rst new/sphinx-issues-3.0.1/tests/source/index.rst --- old/sphinx-issues-1.2.0/tests/source/index.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/tests/source/index.rst 2022-01-11 17:41:14.000000000 +0100 @@ -0,0 +1,7 @@ + +Welcome to sphinx-issues's documentation! +========================================= + + +.. include:: examples.rst +.. include:: ../README.rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/tests/test_sphinx_issues.py new/sphinx-issues-3.0.1/tests/test_sphinx_issues.py --- old/sphinx-issues-1.2.0/tests/test_sphinx_issues.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/tests/test_sphinx_issues.py 2022-01-11 17:41:14.000000000 +0100 @@ -0,0 +1,358 @@ +import subprocess +import sys +from pathlib import Path +from shutil import rmtree +from tempfile import mkdtemp +from unittest.mock import Mock + +import pytest +import sphinx.application +from sphinx_issues import commit_role, cve_role, cwe_role, issue_role, pr_role +from sphinx_issues import setup as issues_setup +from sphinx_issues import user_role + + +BASE_DIR = Path(__file__).parent.absolute() + + +@pytest.fixture( + params=[ + # Parametrize config + {"issues_github_path": "marshmallow-code/marshmallow"}, + {"issues_default_group_project": "marshmallow-code/marshmallow"}, + { + "issues_uri": "https://github.com/marshmallow-code/marshmallow/issues/{issue}", + "issues_pr_uri": "https://github.com/marshmallow-code/marshmallow/pull/{pr}", + "issues_commit_uri": "https://github.com/marshmallow-code/marshmallow/commit/{commit}", + }, + ] +) +def app(request): + src, doctree, confdir, outdir = [mkdtemp() for _ in range(4)] + sphinx.application.Sphinx._log = lambda self, message, wfile, nonl=False: None + app = sphinx.application.Sphinx( + srcdir=src, confdir=None, outdir=outdir, doctreedir=doctree, buildername="html" + ) + issues_setup(app) + # Stitch together as the sphinx app init() usually does w/ real conf files + app.config._raw_config = request.param + try: + app.config.init_values() + except TypeError: + app.config.init_values(lambda x: x) + yield app + [rmtree(x) for x in (src, doctree, confdir, outdir)] + + +@pytest.fixture() +def inliner(app): + return Mock(document=Mock(settings=Mock(env=Mock(app=app)))) + + +@pytest.mark.parametrize( + ("role", "role_name", "text", "expected_text", "expected_url"), + [ + ( + issue_role, + "issue", + "42", + "#42", + "https://github.com/marshmallow-code/marshmallow/issues/42", + ), + ( + issue_role, + "issue", + "Hard Issue <42>", + "Hard Issue", + "https://github.com/marshmallow-code/marshmallow/issues/42", + ), + ( + issue_role, + "issue", + "Not my business <foo/bar#42>", + "Not my business", + "https://github.com/foo/bar/issues/42", + ), + ( + pr_role, + "pr", + "42", + "#42", + "https://github.com/marshmallow-code/marshmallow/pull/42", + ), + (user_role, "user", "sloria", "@sloria", "https://github.com/sloria"), + ( + user_role, + "user", + "Steven Loria <sloria>", + "Steven Loria", + "https://github.com/sloria", + ), + ( + cve_role, + "cve", + "CVE-2018-17175", + "CVE-2018-17175", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17175", + ), + ( + cwe_role, + "cve", + "CWE-787", + "CWE-787", + "https://cwe.mitre.org/data/definitions/787.html", + ), + ( + commit_role, + "commit", + "123abc456def", + "@123abc4", + "https://github.com/marshmallow-code/marshmallow/commit/123abc456def", + ), + # External issue + ( + issue_role, + "issue", + "sloria/webargs#42", + "sloria/webargs#42", + "https://github.com/sloria/webargs/issues/42", + ), + # External PR + ( + pr_role, + "pr", + "sloria/webargs#42", + "sloria/webargs#42", + "https://github.com/sloria/webargs/pull/42", + ), + # External commit + ( + commit_role, + "commit", + "sloria/webargs@abc123def456", + "sloria/webargs@abc123d", + "https://github.com/sloria/webargs/commit/abc123def456", + ), + ], +) +def test_roles(inliner, role, role_name, text, expected_text, expected_url): + result = role(role_name, rawtext="", text=text, lineno=None, inliner=inliner) + link = result[0][0] + assert link.astext() == expected_text + assert link.attributes["refuri"] == expected_url + + +def test_issue_role_multiple(inliner): + result = issue_role( + name=None, rawtext="", text="a title <42>,43", inliner=inliner, lineno=None + ) + link1 = result[0][0] + assert link1.astext() == "a title" + issue_url = "https://github.com/marshmallow-code/marshmallow/issues/" + assert link1.attributes["refuri"] == issue_url + "42" + + sep = result[0][1] + assert sep.astext() == ", " + + link2 = result[0][2] + assert link2.astext() == "#43" + assert link2.attributes["refuri"] == issue_url + "43" + + +def test_issue_role_multiple_with_external(inliner): + result = issue_role( + "issue", rawtext="", text="42,sloria/konch#43", inliner=inliner, lineno=None + ) + link1 = result[0][0] + assert link1.astext() == "#42" + issue_url = "https://github.com/marshmallow-code/marshmallow/issues/42" + assert link1.attributes["refuri"] == issue_url + + sep = result[0][1] + assert sep.astext() == ", " + + link2 = result[0][2] + assert link2.astext() == "sloria/konch#43" + assert link2.attributes["refuri"] == "https://github.com/sloria/konch/issues/43" + + +@pytest.fixture +def app_custom_uri(): + src, doctree, confdir, outdir = [mkdtemp() for _ in range(4)] + sphinx.application.Sphinx._log = lambda self, message, wfile, nonl=False: None + app = sphinx.application.Sphinx( + srcdir=src, confdir=None, outdir=outdir, doctreedir=doctree, buildername="html" + ) + issues_setup(app) + # Stitch together as the sphinx app init() usually does w/ real conf files + app.config._raw_config = { + "issues_default_group_project": "myteam/super_great_project", + "issues_uri": "https://gitlab.company.com/{group}/{project}/-/issues/{issue}", + "issues_prefix": "#", + "issues_pr_uri": "https://gitlab.company.com/{group}/{project}/-/merge_requests/{pr}", + "issues_pr_prefix": "!", + "issues_commit_uri": "https://gitlab.company.com/{group}/{project}/-/commit/{commit}", + "issues_commit_prefix": "@", + "issues_user_uri": "https://gitlab.company.com/{user}", + "issues_user_prefix": "@", + } + try: + app.config.init_values() + except TypeError: + app.config.init_values(lambda x: x) + yield app + [rmtree(x) for x in (src, doctree, confdir, outdir)] + + +@pytest.fixture() +def inliner_custom_uri(app_custom_uri): + return Mock(document=Mock(settings=Mock(env=Mock(app=app_custom_uri)))) + + +@pytest.mark.parametrize( + ("role", "role_name", "text", "expected_text", "expected_url"), + [ + ( + issue_role, + "issue", + "42", + "#42", + "https://gitlab.company.com/myteam/super_great_project/-/issues/42", + ), + ( + issue_role, + "issue", + "Hard Issue <42>", + "Hard Issue", + "https://gitlab.company.com/myteam/super_great_project/-/issues/42", + ), + ( + issue_role, + "issue", + "Not my business <foo/bar#42>", + "Not my business", + "https://gitlab.company.com/foo/bar/-/issues/42", + ), + ( + pr_role, + "pr", + "42", + "!42", + "https://gitlab.company.com/myteam/super_great_project/-/merge_requests/42", + ), + (user_role, "user", "sloria", "@sloria", "https://gitlab.company.com/sloria"), + ( + user_role, + "user", + "Steven Loria <sloria>", + "Steven Loria", + "https://gitlab.company.com/sloria", + ), + ( + commit_role, + "commit", + "123abc456def", + "@123abc4", + "https://gitlab.company.com/myteam/super_great_project/-/commit/123abc456def", + ), + # External issue + ( + issue_role, + "issue", + "sloria/webargs#42", + "sloria/webargs#42", + "https://gitlab.company.com/sloria/webargs/-/issues/42", + ), + # External PR + ( + pr_role, + "pr", + "sloria/webargs#42", + "sloria/webargs!42", + "https://gitlab.company.com/sloria/webargs/-/merge_requests/42", + ), + # External commit + ( + commit_role, + "commit", + "sloria/webargs@abc123def456", + "sloria/webargs@abc123d", + "https://gitlab.company.com/sloria/webargs/-/commit/abc123def456", + ), + ], +) +def test_roles_custom_uri( + inliner_custom_uri, role, role_name, text, expected_text, expected_url +): + result = role( + role_name, rawtext="", text=text, lineno=None, inliner=inliner_custom_uri + ) + link = result[0][0] + assert link.astext() == expected_text + assert link.attributes["refuri"] == expected_url + + +@pytest.fixture +def tmp_doc_build_folder(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: + """Generate a temporary source folder and chdir in it. Return the build folder""" + source = tmp_path / "source" + build = tmp_path / "build" + static = source / "_static" + for folder in (source, build, static): + folder.mkdir() + conf_py = BASE_DIR / "source" / "conf.py" + examples_rst = BASE_DIR / "source" / "examples.rst" + + source.joinpath("conf.py").write_bytes(conf_py.read_bytes()) + source.joinpath("index.rst").write_bytes(examples_rst.read_bytes()) + + monkeypatch.chdir(source) + return build + + +def test_sphinx_build_integration(tmp_doc_build_folder: Path): + """Ensure that a simulated complete sphinx run works as expected""" + subprocess.run( + [ + Path(sys.executable).parent.joinpath("sphinx-build"), + "-b", + "html", + "-W", # turn warnings into errors + "-E", # force rebuild of environment (even if we work in tmp) + ".", + str(tmp_doc_build_folder), + ], + check=True, + ) + + created = tmp_doc_build_folder / "index.html" + assert created.exists() and created.is_file() + content = created.read_text() + issue_url = "https://gitlab.company.com/myteam/super_great_project/-/issues/" + other_issue_url = "https://gitlab.company.com/sloria/konch/-/issues/" + pr_url = "https://gitlab.company.com/myteam/super_great_project/-/merge_requests/" + user_url = "https://gitlab.company.com/" + + # We could do something fancy like an HTML parser or regex: + # Instead we keep it simple + expected_strings = ( + ( + f"See issues " + f'<a class="reference external" href="{issue_url}12">#12</a>, ' + f'<a class="reference external" href="{issue_url}13">#13</a>' + ), + ( + f"See other issues " + f'<a class="reference external" href="{other_issue_url}45">sloria/konch#45</a>,' + f' <a class="reference external" href="{issue_url}46">#46</a>' + ), + ( + f'See PR <a class="reference external" href="{pr_url}58">!58</a>, ' + f'thanks <a class="reference external" href="{user_url}kound">@kound</a>' + ), + ) + # Ensure that we do no check character wise but line wise + assert len(expected_strings) == 3 + + for expected in expected_strings: + assert expected in content diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sphinx-issues-1.2.0/tox.ini new/sphinx-issues-3.0.1/tox.ini --- old/sphinx-issues-1.2.0/tox.ini 2018-12-26 17:05:00.000000000 +0100 +++ new/sphinx-issues-3.0.1/tox.ini 2022-01-11 17:41:14.000000000 +0100 @@ -1,13 +1,14 @@ [tox] -envlist = lint,py27,py35,py36,py37 +envlist = lint,py36,py37,py38,py39,py310 [testenv] extras = tests commands = pytest {posargs} [testenv:lint] -extras = lint -commands = pre-commit run --all-files --show-diff-on-failure +deps = pre-commit~=2.7 +skip_install = true +commands = pre-commit run --all-files ; Below tasks are for development only (not run in CI) [testenv:watch-readme]