From 5f33014566d4639c2c56d82bbd2fce5bb2ba024b Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:25:32 +0200 Subject: [PATCH 1/8] Merge branch origin/main into towncrier --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 739ee312d3..022cab9e29 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -131,7 +131,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ["_build", "data/**"] +exclude_patterns = ["_build", "data/**", "whatsnew/fragments"] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None From 97e6bef0879520c6a83fa25303072cfc2725a385 Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:26:03 +0200 Subject: [PATCH 2/8] Add `towncrier` to requirements --- doc/requirements.txt | 1 + requirements_test_min.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/requirements.txt b/doc/requirements.txt index 11a95e4d28..295f829a4c 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,5 +1,6 @@ Sphinx==5.0.2 sphinx-reredirects<1 myst-parser~=0.18 +towncrier~=21.9 furo==2022.6.21 -e . diff --git a/requirements_test_min.txt b/requirements_test_min.txt index 18f27d5eda..8202b4d775 100644 --- a/requirements_test_min.txt +++ b/requirements_test_min.txt @@ -5,4 +5,5 @@ typing-extensions~=4.3 pytest~=7.1 pytest-benchmark~=3.4 pytest-timeout~=2.1 +towncrier~=21.9 requests From 15858cb4b732d4adbfd86d410f8cb4d747408c91 Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:27:45 +0200 Subject: [PATCH 3/8] Add configuration for `towncrier` --- .pyenchant_pylint_custom_dict.txt | 2 ++ doc/whatsnew/fragments/_template.rst | 38 +++++++++++++++++++++ towncrier.toml | 50 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 doc/whatsnew/fragments/_template.rst create mode 100644 towncrier.toml diff --git a/.pyenchant_pylint_custom_dict.txt b/.pyenchant_pylint_custom_dict.txt index c827856b53..f37c3ad638 100644 --- a/.pyenchant_pylint_custom_dict.txt +++ b/.pyenchant_pylint_custom_dict.txt @@ -212,6 +212,7 @@ mymodule mypy namedtuple namespace +newsfile newstyle nl nodename @@ -320,6 +321,7 @@ tokenizer toml tomlkit toplevel +towncrier tp truthness tryexcept diff --git a/doc/whatsnew/fragments/_template.rst b/doc/whatsnew/fragments/_template.rst new file mode 100644 index 0000000000..115dc57661 --- /dev/null +++ b/doc/whatsnew/fragments/_template.rst @@ -0,0 +1,38 @@ +{% set title = "What's new in Pylint " + versiondata.version + "?" %} +{{ title }} +{{ underlines[0] * (title|length) }} +Release date: {{ versiondata.date }} + +{% for section, _ in sections.items() %} +{% set underline = underlines[0] %}{% if section %}{{section}} +{{ underline * section|length }}{% set underline = underlines[1] %} + +{% endif %} + +{% if sections[section] %} +{% for category, val in definitions.items() if category in sections[section]%} +{{ definitions[category]['name'] }} +{{ underline * definitions[category]['name']|length }} + +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category].items() %} +- {{ text }} ({{ values|join(', ') }}) +{% endfor %} + +{% else %} +- {{ sections[section][category]['']|join(', ') }} + +{% endif %} +{% if sections[section][category]|length == 0 %} +No significant changes. + +{% else %} +{% endif %} + +{% endfor %} +{% else %} +No significant changes. + + +{% endif %} +{% endfor %} diff --git a/towncrier.toml b/towncrier.toml new file mode 100644 index 0000000000..52b4072b3b --- /dev/null +++ b/towncrier.toml @@ -0,0 +1,50 @@ +[tool.towncrier] +version = "2.15.0" +directory = "doc/whatsnew/fragments" +filename = "doc/whatsnew/2/2.15/index.rst" +template = "doc/whatsnew/fragments/_template.rst" +issue_format = "`#{issue} `_" + +# Definition of fragment types. +# TODO: with the next towncrier release (21.9.1) it will be possible to define +# custom types as toml tables: +# https://github.com/twisted/towncrier#further-options +[[tool.towncrier.type]] +directory = "new_check" +name = "New Checks" +showcontent = true + +[[tool.towncrier.type]] +directory = "removed_check" +name = "Removed Checks" +showcontent = true + +[[tool.towncrier.type]] +directory = "extension" +name = "Extensions" +showcontent = true + +[[tool.towncrier.type]] +directory = "false_positive" +name = "False Positives Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "false_negative" +name = "False Negatives Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "bugfix" +name = "Other Bug Fixes" +showcontent = true + +[[tool.towncrier.type]] +directory = "other" +name = "Other Changes" +showcontent = true + +[[tool.towncrier.type]] +directory = "internal" +name = "Internal Changes" +showcontent = true From 54e54ad099c321fe825ed7328ffc54f794a339fe Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:29:08 +0200 Subject: [PATCH 4/8] Add pre-commit hook to verify format of news fragment --- .pre-commit-config.yaml | 7 +++ script/check_newsfragments.py | 84 +++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 script/check_newsfragments.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bc3caf3a7d..ce82150441 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -85,6 +85,13 @@ repos: language: system types: [text] files: ^(doc/whatsnew/\d+\.\d+\.rst) + - id: check-newsfragments + name: Check newsfragments + entry: python3 -m script.check_newsfragments + language: system + types: [text] + files: ^(doc/whatsnew/fragments) + exclude: doc/whatsnew/fragments/_.*.rst - repo: https://github.com/rstcheck/rstcheck rev: "v6.0.0.post1" hooks: diff --git a/script/check_newsfragments.py b/script/check_newsfragments.py new file mode 100644 index 0000000000..cb5544f0b1 --- /dev/null +++ b/script/check_newsfragments.py @@ -0,0 +1,84 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +"""Small script to check the formatting of news fragments for towncrier. +Used by pre-commit. +""" + +from __future__ import annotations + +import argparse +import re +import sys +from pathlib import Path +from re import Pattern + +VALID_ISSUES_KEYWORDS = [ + "Refs", + "Closes", + "Follow-up in", + "Fixes part of", +] +ISSUES_KEYWORDS = "|".join(VALID_ISSUES_KEYWORDS) +VALID_CHANGELOG_PATTERN = rf"(?P(.*\n)*(.*\.\n))\n(?P({ISSUES_KEYWORDS}) (PyCQA/astroid)?#(?P\d+))" +VALID_CHANGELOG_COMPILED_PATTERN: Pattern[str] = re.compile( + VALID_CHANGELOG_PATTERN, flags=re.MULTILINE +) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "filenames", + nargs="*", + metavar="FILES", + help="File names to check", + ) + parser.add_argument("--verbose", "-v", action="count", default=0) + args = parser.parse_args(argv) + is_valid = True + for filename in args.filenames: + is_valid &= check_file(Path(filename), args.verbose) + return 0 if is_valid else 1 + + +def check_file(file: Path, verbose: bool) -> bool: + """Check that a file contains a valid changelog entry.""" + with open(file, encoding="utf8") as f: + content = f.read() + match = VALID_CHANGELOG_COMPILED_PATTERN.match(content) + if match: + issue = match.group("issue") + if file.stem != issue: + print( + f"{file} must be named '{issue}.', after the issue it references." + ) + return False + if verbose: + print(f"Checked '{file}': LGTM 🤖👍") + return True + print( + f"""\ +{file}: does not respect the standard format 🤖👎 + +The standard format is: + + + + # + +Where can be one of: {', '.join(VALID_ISSUES_KEYWORDS)} + +For example: + +``pylint.x.y`` is now a private API. + +Refs #1234 +""" + ) + return False + + +if __name__ == "__main__": + sys.exit(main()) From 93e0ce143c9146d89064301f5c687138aa3f0c43 Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:30:13 +0200 Subject: [PATCH 5/8] Update documentation for usage of `towncrier` --- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++---- .../contributor_guide/contribute.rst | 12 +++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 90a6b12c3a..82bd53ea5a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,10 +4,10 @@ Thank you for submitting a PR to pylint! To ease the process of reviewing your PR, do make sure to complete the following boxes. - [ ] Write a good description on what the PR does. -- [ ] Add an entry to the change log describing the change in - `doc/whatsnew/2/2.15/index.rst` (or ``doc/whatsnew/2/2.14/full.rst`` - if the change needs backporting in 2.14). If necessary you can write - details or offer examples on how the new change is supposed to work. +- [ ] Create a news fragment with `towncrier create .` which will be + included in the changelog. `` can be one of: new_check, removed_check, extension, + false_positive, false_negative, bugfix, other, internal. If necessary you can write + details or offer examples on how the new change is supposed to work. - [ ] If you used multiple emails or multiple names when contributing, add your mails and preferred name in ``script/.contributors_aliases.json`` --> diff --git a/doc/development_guide/contributor_guide/contribute.rst b/doc/development_guide/contributor_guide/contribute.rst index 4ad52a6247..32de201060 100644 --- a/doc/development_guide/contributor_guide/contribute.rst +++ b/doc/development_guide/contributor_guide/contribute.rst @@ -59,11 +59,17 @@ your patch gets accepted: - We recommend using Python 3.8 or higher for development of Pylint as it gives you access to the latest ``ast`` parser. + - Install the dev dependencies, see :ref:`contributor_install`. + - Use our test suite and write new tests, see :ref:`contributor_testing`. -- Add an entry to the change log describing the change in `doc/whatsnew/2/2.15/index.rst` - (or ``doc/whatsnew/2/2.14/full.rst`` if the change needs backporting in 2.14). - If necessary you can write details or offer examples on how the new change is supposed to work. + +.. keep this in sync with the description of PULL_REQUEST_TEMPLATE.md! + +- Create a news fragment with `towncrier create .` which will be + included in the changelog. `` can be one of: new_check, removed_check, extension, + false_positive, false_negative, bugfix, other, internal. If necessary you can write + details or offer examples on how the new change is supposed to work. - Document your change, if it is a non-trivial one. From 9345ba5cd0f521fe0eefe77263ffcfdec12e30ba Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:30:56 +0200 Subject: [PATCH 6/8] Create pipeline for checking existence of changelog and update cache version --- .github/workflows/changelog.yml | 62 ++++++++++++++++++++++++++ .github/workflows/checks.yaml | 2 +- .github/workflows/primer-test.yaml | 2 +- .github/workflows/primer_comment.yaml | 2 +- .github/workflows/primer_run_main.yaml | 2 +- .github/workflows/primer_run_pr.yaml | 2 +- .github/workflows/tests.yaml | 2 +- .readthedocs.yaml | 3 ++ 8 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/changelog.yml diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000000..7dbbbd31fb --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,62 @@ +name: changelog + +on: + pull_request: + types: [opened, synchronize, labeled, unlabeled, reopened] + +env: + # Also change CACHE_VERSION in the other workflows + CACHE_VERSION: 20 + DEFAULT_PYTHON: "3.10" + +jobs: + check-changelog: + if: contains(github.event.pull_request.labels.*.name, 'skip news :mute:') != true + name: Changelog Entry Check + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out code from GitHub + uses: actions/checkout@v3.0.2 + with: + # `towncrier check` runs `git diff --name-only origin/main...`, which + # needs a non-shallow clone. + fetch-depth: 0 + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v4.0.0 + with: + python-version: ${{ env.DEFAULT_PYTHON }} + - name: Generate partial Python venv restore key + id: generate-python-key + run: >- + echo "::set-output name=key::base-venv-${{ env.CACHE_VERSION }}-${{ + hashFiles('setup.cfg', 'requirements_test.txt', 'requirements_test_min.txt') + }}" + - name: Restore Python virtual environment + id: cache-venv + uses: actions/cache@v3.0.4 + with: + path: venv + key: >- + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ + steps.generate-python-key.outputs.key }} + restore-keys: | + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}- + - name: Create Python virtual environment + if: steps.cache-venv.outputs.cache-hit != 'true' + run: | + python -m venv venv + . venv/bin/activate + python -m pip install -U pip setuptools wheel + pip install -U -r requirements_test.txt + pip install -U -r doc/requirements.txt + - name: Emit warning if news fragment is missing + env: + BASE_BRANCH: ${{ github.base_ref }} + run: | + # Fetch the pull request' base branch so towncrier will be able to + # compare the current branch with the base branch. + git fetch --no-tags origin +refs/heads/${BASE_BRANCH}:refs/remotes/origin/${BASE_BRANCH} + . venv/bin/activate + towncrier check --compare-with origin/${{ github.base_ref }} diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index adc2e10c8e..196a0115be 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -8,7 +8,7 @@ on: pull_request: ~ env: - CACHE_VERSION: 1 + CACHE_VERSION: 20 DEFAULT_PYTHON: "3.10" PRE_COMMIT_CACHE: ~/.cache/pre-commit diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml index bda9f51f11..a6a4ce4245 100644 --- a/.github/workflows/primer-test.yaml +++ b/.github/workflows/primer-test.yaml @@ -13,7 +13,7 @@ on: - ".github/workflows/primer-test.yaml" env: - CACHE_VERSION: 1 + CACHE_VERSION: 20 concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/primer_comment.yaml b/.github/workflows/primer_comment.yaml index ebef77e77b..3114db3f9a 100644 --- a/.github/workflows/primer_comment.yaml +++ b/.github/workflows/primer_comment.yaml @@ -13,7 +13,7 @@ on: - completed env: - CACHE_VERSION: 1 + CACHE_VERSION: 20 permissions: contents: read diff --git a/.github/workflows/primer_run_main.yaml b/.github/workflows/primer_run_main.yaml index c231e1ff2b..8482c512e9 100644 --- a/.github/workflows/primer_run_main.yaml +++ b/.github/workflows/primer_run_main.yaml @@ -15,7 +15,7 @@ concurrency: cancel-in-progress: true env: - CACHE_VERSION: 1 + CACHE_VERSION: 20 jobs: run-primer: diff --git a/.github/workflows/primer_run_pr.yaml b/.github/workflows/primer_run_pr.yaml index efe88c05cd..c3f039ed66 100644 --- a/.github/workflows/primer_run_pr.yaml +++ b/.github/workflows/primer_run_pr.yaml @@ -24,7 +24,7 @@ concurrency: cancel-in-progress: true env: - CACHE_VERSION: 1 + CACHE_VERSION: 20 jobs: run-primer: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index dfae0e72ce..4fbe0b1b3c 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -10,7 +10,7 @@ on: - doc/data/messages/** env: - CACHE_VERSION: 1 + CACHE_VERSION: 20 jobs: tests-linux: diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 0fec8925dd..9f7c35450e 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -14,3 +14,6 @@ build: os: ubuntu-20.04 tools: python: "3.8" + jobs: + pre_build: + - towncrier build --yes --date TBA From caaaa404c0264a5c290bc9608502a7a8f80752a8 Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:31:41 +0200 Subject: [PATCH 7/8] Edit `bump_changelog.py` to use `towncrier` and update its configuration --- script/bump_changelog.py | 245 ++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 145 deletions(-) diff --git a/script/bump_changelog.py b/script/bump_changelog.py index f27195bdcd..420ca1d22a 100644 --- a/script/bump_changelog.py +++ b/script/bump_changelog.py @@ -2,167 +2,122 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt -# ORIGINAL here: https://github.com/PyCQA/astroid/blob/main/script/bump_changelog.py -# DO NOT MODIFY DIRECTLY - -"""This script permits to upgrade the changelog in astroid or pylint when releasing a version.""" -# pylint: disable=logging-fstring-interpolation - +"""This script updates towncrier.toml and creates a new newsfile and intermediate +folders if necessary. +""" from __future__ import annotations import argparse -import enum -import logging -from datetime import datetime +import re from pathlib import Path +from subprocess import check_call + +NEWSFILE_PATTERN = re.compile(r"doc/whatsnew/\d/\d.\d+/index\.rst") +NEWSFILE_PATH = "doc/whatsnew/{major}/{major}.{minor}/index.rst" +TOWNCRIER_CONFIG_FILE = Path("towncrier.toml") +TOWNCRIER_VERSION_PATTERN = re.compile(r"version = \"(\d+\.\d+\.\d+)\"") + +NEWSFILE_CONTENT_TEMPLATE = """ +*************************** + What's New in Pylint {major}.{minor} +*************************** -# TODO: 2.15.0 Modify the way we handle the patch version -# release notes -DEFAULT_CHANGELOG_PATH = Path("doc/whatsnew/2/2.14/full.rst") +.. toctree:: + :maxdepth: 2 -RELEASE_DATE_TEXT = "Release date: TBA" -WHATS_NEW_TEXT = "What's New in Pylint" -TODAY = datetime.now() -FULL_WHATS_NEW_TEXT = WHATS_NEW_TEXT + " {version}?" -NEW_RELEASE_DATE_MESSAGE = f"Release date: {TODAY.strftime('%Y-%m-%d')}" +:Release:{major}.{minor} +:Date: TBA + +Summary -- Release highlights +============================= + + +.. towncrier release notes start +""" def main() -> None: - parser = argparse.ArgumentParser(__doc__) - parser.add_argument("version", help="The version we want to release") + parser = argparse.ArgumentParser() + parser.add_argument("version", help="The new version to set") parser.add_argument( - "-v", "--verbose", action="store_true", default=False, help="Logging or not" + "--dry-run", + action="store_true", + help="Just show what would be done, don't write anything", ) args = parser.parse_args() - if args.verbose: - logging.basicConfig(level=logging.DEBUG) - logging.debug(f"Launching bump_changelog with args: {args}") + if "dev" in args.version: - return - with open(DEFAULT_CHANGELOG_PATH, encoding="utf-8") as f: - content = f.read() - content = transform_content(content, args.version) - with open(DEFAULT_CHANGELOG_PATH, "w", encoding="utf-8") as f: - f.write(content) - - -class VersionType(enum.Enum): - MAJOR = 0 - MINOR = 1 - PATCH = 2 - - -def get_next_version(version: str, version_type: VersionType) -> str: - new_version = version.split(".") - part_to_increase = new_version[version_type.value] - if "-" in part_to_increase: - part_to_increase = part_to_increase.split("-")[0] - for i in range(version_type.value, 3): - new_version[i] = "0" - new_version[version_type.value] = str(int(part_to_increase) + 1) - return ".".join(new_version) - - -def get_next_versions(version: str, version_type: VersionType) -> list[str]: - - if version_type == VersionType.PATCH: - # "2.6.1" => ["2.6.2"] - return [get_next_version(version, VersionType.PATCH)] - if version_type == VersionType.MINOR: - # "2.6.0" => ["2.7.0", "2.6.1"] - assert version.endswith(".0"), f"{version} does not look like a minor version" - else: - # "3.0.0" => ["3.1.0", "3.0.1"] - assert version.endswith(".0.0"), f"{version} does not look like a major version" - next_minor_version = get_next_version(version, VersionType.MINOR) - next_patch_version = get_next_version(version, VersionType.PATCH) - logging.debug(f"Getting the new version for {version} - {version_type.name}") - return [next_minor_version, next_patch_version] - - -def get_version_type(version: str) -> VersionType: - if version.endswith(".0.0"): - version_type = VersionType.MAJOR - elif version.endswith(".0"): - version_type = VersionType.MINOR - else: - version_type = VersionType.PATCH - return version_type - - -def get_whats_new( - version: str, add_date: bool = False, change_date: bool = False -) -> str: - whats_new_text = FULL_WHATS_NEW_TEXT.format(version=version) - result = [whats_new_text, "-" * len(whats_new_text)] - if add_date and change_date: - result += [NEW_RELEASE_DATE_MESSAGE] - elif add_date: - result += [RELEASE_DATE_TEXT] - elif change_date: - raise ValueError("Can't use change_date=True with add_date=False") - logging.debug( - f"version='{version}', add_date='{add_date}', change_date='{change_date}': {result}" - ) - return "\n".join(result) - - -def get_all_whats_new(version: str, version_type: VersionType) -> str: - result = "" - for version_ in get_next_versions(version, version_type=version_type): - result += get_whats_new(version_, add_date=True) + "\n" * 4 - return result - - -def transform_content(content: str, version: str) -> str: - version_type = get_version_type(version) - next_version = get_next_version(version, version_type) - old_date = get_whats_new(version, add_date=True) - new_date = get_whats_new(version, add_date=True, change_date=True) - next_version_with_date = get_all_whats_new(version, version_type) - do_checks(content, next_version, version, version_type) - index = content.find(old_date) - logging.debug(f"Replacing\n'{old_date}'\nby\n'{new_date}'\n") - content = content.replace(old_date, new_date) - end_content = content[index:] - content = content[:index] - logging.debug(f"Adding:\n'{next_version_with_date}'\n") - content += next_version_with_date + end_content - return content - - -def do_checks(content, next_version, version, version_type): - err = "in the changelog, fix that first!" - NEW_VERSION_ERROR_MSG = ( - # pylint: disable-next=consider-using-f-string - "The text for this version '{version}' did not exists %s" - % err + print("'-devXY' will be cut from version in towncrier.toml") + match = re.match( + r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-\w+\d*)*", args.version ) - NEXT_VERSION_ERROR_MSG = ( - # pylint: disable-next=consider-using-f-string - "The text for the next version '{version}' already exists %s" - % err - ) - wn_next_version = get_whats_new(next_version) - wn_this_version = get_whats_new(version) - # There is only one field where the release date is TBA - if version_type in [VersionType.MAJOR, VersionType.MINOR]: - assert ( - content.count(RELEASE_DATE_TEXT) <= 1 - ), f"There should be only one release date 'TBA' ({version}) {err}" - else: - next_minor_version = get_next_version(version, VersionType.MINOR) - assert ( - content.count(RELEASE_DATE_TEXT) <= 2 - ), f"There should be only two release dates 'TBA' ({version} and {next_minor_version}) {err}" - # There is already a release note for the version we want to release - assert content.count(wn_this_version) == 1, NEW_VERSION_ERROR_MSG.format( - version=version + if not match: + print( + "Fatal error - new version did not match the " + "expected format (major.minor.patch[.*]). Abort!" + ) + return + major, minor, patch, suffix = match.groups() + new_version = f"{major}.{minor}.{patch}" + + new_newsfile = NEWSFILE_PATH.format(major=major, minor=minor) + create_new_newsfile_if_necessary(new_newsfile, major, minor, args.dry_run) + patch_towncrier_toml(new_newsfile, new_version, args.dry_run) + build_changelog(suffix, args.dry_run) + + +def create_new_newsfile_if_necessary( + new_newsfile: str, major: str, minor: str, dry_run: bool +) -> None: + new_newsfile_path = Path(new_newsfile) + if new_newsfile_path.exists(): + return + + # create new file and add boiler plate content + if dry_run: + print( + f"Dry run enabled - would create file {new_newsfile} " + "and intermediate folders" + ) + return + + print("Creating new newsfile:", new_newsfile) + new_newsfile_path.parent.mkdir(parents=True, exist_ok=True) + new_newsfile_path.touch() + new_newsfile_path.write_text( + NEWSFILE_CONTENT_TEMPLATE.format(major=major, minor=minor), + encoding="utf8", ) - # There is no release notes for the next version - assert content.count(wn_next_version) == 0, NEXT_VERSION_ERROR_MSG.format( - version=next_version + + # tbump does not add and commit new files, so we add it ourselves + print("Adding new newsfile to git") + check_call(["git", "add", new_newsfile]) + + +def patch_towncrier_toml(new_newsfile: str, version: str, dry_run: bool) -> None: + file_content = TOWNCRIER_CONFIG_FILE.read_text(encoding="utf-8") + patched_newsfile_path = NEWSFILE_PATTERN.sub(new_newsfile, file_content) + new_file_content = TOWNCRIER_VERSION_PATTERN.sub( + f'version = "{version}"', patched_newsfile_path ) + if dry_run: + print("Dry run enabled - this is what I would write:\n") + print(new_file_content) + return + TOWNCRIER_CONFIG_FILE.write_text(new_file_content, encoding="utf-8") + + +def build_changelog(suffix: str | None, dry_run: bool) -> None: + if suffix: + print("Not a release version, skipping changelog generation") + return + + if dry_run: + print("Dry run enabled - not building changelog") + return + + print("Building changelog") + check_call(["towncrier", "build", "--yes"]) if __name__ == "__main__": From 9b89303d4e730867f750f22580d69efd6d27e257 Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:32:16 +0200 Subject: [PATCH 8/8] Replace existing changelog for 2.15 with news fragments --- doc/whatsnew/2/2.15/index.rst | 91 +--------------------- doc/whatsnew/fragments/4624.false_negative | 3 + doc/whatsnew/fragments/4951.false_positive | 4 + doc/whatsnew/fragments/5653.false_negative | 4 + doc/whatsnew/fragments/6643.false_negative | 3 + doc/whatsnew/fragments/6648.false_negative | 4 + doc/whatsnew/fragments/6780.new_check | 4 + doc/whatsnew/fragments/6812.false_negative | 3 + doc/whatsnew/fragments/6883.false_negative | 4 + doc/whatsnew/fragments/6891.internal | 5 ++ doc/whatsnew/fragments/6905.internal | 3 + doc/whatsnew/fragments/6909.false_negative | 4 + doc/whatsnew/fragments/6953.other | 3 + doc/whatsnew/fragments/6974.other | 3 + doc/whatsnew/fragments/7034.false_negative | 5 ++ doc/whatsnew/fragments/7153.other | 3 + 16 files changed, 56 insertions(+), 90 deletions(-) create mode 100644 doc/whatsnew/fragments/4624.false_negative create mode 100644 doc/whatsnew/fragments/4951.false_positive create mode 100644 doc/whatsnew/fragments/5653.false_negative create mode 100644 doc/whatsnew/fragments/6643.false_negative create mode 100644 doc/whatsnew/fragments/6648.false_negative create mode 100644 doc/whatsnew/fragments/6780.new_check create mode 100644 doc/whatsnew/fragments/6812.false_negative create mode 100644 doc/whatsnew/fragments/6883.false_negative create mode 100644 doc/whatsnew/fragments/6891.internal create mode 100644 doc/whatsnew/fragments/6905.internal create mode 100644 doc/whatsnew/fragments/6909.false_negative create mode 100644 doc/whatsnew/fragments/6953.other create mode 100644 doc/whatsnew/fragments/6974.other create mode 100644 doc/whatsnew/fragments/7034.false_negative create mode 100644 doc/whatsnew/fragments/7153.other diff --git a/doc/whatsnew/2/2.15/index.rst b/doc/whatsnew/2/2.15/index.rst index 3f3e34ba88..853c0de71c 100644 --- a/doc/whatsnew/2/2.15/index.rst +++ b/doc/whatsnew/2/2.15/index.rst @@ -14,93 +14,4 @@ Summary -- Release highlights * We improved ``pylint``'s handling of namespace packages. More packages should be linted without resorting to using the ``-recursive=y`` option. - -New checkers -============ - -Added new checker ``missing-timeout`` to warn of default timeout values that could cause -a program to be hanging indefinitely. - - -Removed checkers -================ - - -Extensions -========== - - -False positives fixed -===================== - -* Don't report ``unsupported-binary-operation`` on Python <= 3.9 when using the ``|`` operator - with types, if one has a metaclass that overloads ``__or__`` or ``__ror__`` as appropriate. - - Closes #4951 - -False negatives fixed -===================== - -* Emit ``modified-iterating-list`` and analogous messages for dicts and sets when iterating - literals, or when using the ``del`` keyword. - - Closes #6648 - -* Emit ``using-constant-test`` when testing the truth value of a variable or call result - holding a generator. - - Closes #6909 - -* Emit ``used-before-assignment`` for self-referencing named expressions (``:=``) lacking - prior assignments. - - Closes #5653 - -* Emit ``used-before-assignment`` when calling nested functions before assignment. - - Closes #6812 - -* Emit ``used-before-assignment`` when relying on a name that is reimported later in a function. - - Closes #4624 - -* Emit ``used-before-assignment`` for self-referencing assignments under if conditions. - - Closes #6643 - -* Emit ``nonlocal-without-binding`` when a nonlocal name has been assigned at a later point in the same scope. - - Closes #6883 - -* Rename ``unhashable-dict-key`` to ``unhashable-member`` and emit when creating sets and dicts, - not just when accessing dicts. - - Closes #7034, Closes #7055 - - -Other bug fixes -=============== - - -Other Changes -============= - -* ``useless-super-delegation`` has been renamed to ``useless-parent-delegation`` in order to be more generic. - - Closes #6953 - -* Update ``astroid`` to 2.12. - - -Internal changes -================ - -* ``pylint.testutils.primer`` is now a private API. - - Refs #6905 - -* Fixed an issue where it was impossible to update functional tests output when the existing - output was impossible to parse. Instead of raising an error we raise a warning message and - let the functional test fail with a default value. - - Refs #6891 +.. towncrier release notes start diff --git a/doc/whatsnew/fragments/4624.false_negative b/doc/whatsnew/fragments/4624.false_negative new file mode 100644 index 0000000000..ae490216e8 --- /dev/null +++ b/doc/whatsnew/fragments/4624.false_negative @@ -0,0 +1,3 @@ +Emit ``used-before-assignment`` when relying on a name that is reimported later in a function. + +Closes #4624 diff --git a/doc/whatsnew/fragments/4951.false_positive b/doc/whatsnew/fragments/4951.false_positive new file mode 100644 index 0000000000..acf104b369 --- /dev/null +++ b/doc/whatsnew/fragments/4951.false_positive @@ -0,0 +1,4 @@ +Don't report ``unsupported-binary-operation`` on Python <= 3.9 when using the ``|`` operator +with types, if one has a metaclass that overloads ``__or__`` or ``__ror__`` as appropriate. + +Closes #4951 diff --git a/doc/whatsnew/fragments/5653.false_negative b/doc/whatsnew/fragments/5653.false_negative new file mode 100644 index 0000000000..b017c9e07e --- /dev/null +++ b/doc/whatsnew/fragments/5653.false_negative @@ -0,0 +1,4 @@ +Emit ``used-before-assignment`` for self-referencing named expressions (``:=``) lacking +prior assignments. + +Closes #5653 diff --git a/doc/whatsnew/fragments/6643.false_negative b/doc/whatsnew/fragments/6643.false_negative new file mode 100644 index 0000000000..8a7c483def --- /dev/null +++ b/doc/whatsnew/fragments/6643.false_negative @@ -0,0 +1,3 @@ +Emit ``used-before-assignment`` for self-referencing assignments under if conditions. + +Closes #6643 diff --git a/doc/whatsnew/fragments/6648.false_negative b/doc/whatsnew/fragments/6648.false_negative new file mode 100644 index 0000000000..4ce4fd4449 --- /dev/null +++ b/doc/whatsnew/fragments/6648.false_negative @@ -0,0 +1,4 @@ +Emit ``modified-iterating-list`` and analogous messages for dicts and sets when iterating +literals, or when using the ``del`` keyword. + +Closes #6648 diff --git a/doc/whatsnew/fragments/6780.new_check b/doc/whatsnew/fragments/6780.new_check new file mode 100644 index 0000000000..c822ea735e --- /dev/null +++ b/doc/whatsnew/fragments/6780.new_check @@ -0,0 +1,4 @@ +Added new checker ``missing-timeout`` to warn of default timeout values that could cause +a program to be hanging indefinitely. + +Refs #6780 diff --git a/doc/whatsnew/fragments/6812.false_negative b/doc/whatsnew/fragments/6812.false_negative new file mode 100644 index 0000000000..5a7cc8c8f8 --- /dev/null +++ b/doc/whatsnew/fragments/6812.false_negative @@ -0,0 +1,3 @@ +Emit ``used-before-assignment`` when calling nested functions before assignment. + +Closes #6812 diff --git a/doc/whatsnew/fragments/6883.false_negative b/doc/whatsnew/fragments/6883.false_negative new file mode 100644 index 0000000000..965551466c --- /dev/null +++ b/doc/whatsnew/fragments/6883.false_negative @@ -0,0 +1,4 @@ + +Emit ``nonlocal-without-binding`` when a nonlocal name has been assigned at a later point in the same scope. + +Closes #6883 diff --git a/doc/whatsnew/fragments/6891.internal b/doc/whatsnew/fragments/6891.internal new file mode 100644 index 0000000000..03e4179d1d --- /dev/null +++ b/doc/whatsnew/fragments/6891.internal @@ -0,0 +1,5 @@ +Fixed an issue where it was impossible to update functional tests output when the existing +output was impossible to parse. Instead of raising an error we raise a warning message and +let the functional test fail with a default value. + +Refs #6891 diff --git a/doc/whatsnew/fragments/6905.internal b/doc/whatsnew/fragments/6905.internal new file mode 100644 index 0000000000..babca522b7 --- /dev/null +++ b/doc/whatsnew/fragments/6905.internal @@ -0,0 +1,3 @@ +``pylint.testutils.primer`` is now a private API. + +Refs #6905 diff --git a/doc/whatsnew/fragments/6909.false_negative b/doc/whatsnew/fragments/6909.false_negative new file mode 100644 index 0000000000..7618d26492 --- /dev/null +++ b/doc/whatsnew/fragments/6909.false_negative @@ -0,0 +1,4 @@ +Emit ``using-constant-test`` when testing the truth value of a variable or call result +holding a generator. + +Closes #6909 diff --git a/doc/whatsnew/fragments/6953.other b/doc/whatsnew/fragments/6953.other new file mode 100644 index 0000000000..d189400126 --- /dev/null +++ b/doc/whatsnew/fragments/6953.other @@ -0,0 +1,3 @@ +``useless-super-delegation`` has been renamed to ``useless-parent-delegation`` in order to be more generic. + +Closes #6953 diff --git a/doc/whatsnew/fragments/6974.other b/doc/whatsnew/fragments/6974.other new file mode 100644 index 0000000000..5778769485 --- /dev/null +++ b/doc/whatsnew/fragments/6974.other @@ -0,0 +1,3 @@ +Pylint now uses ``towncrier`` for changelog generation. + +Refs #6974 diff --git a/doc/whatsnew/fragments/7034.false_negative b/doc/whatsnew/fragments/7034.false_negative new file mode 100644 index 0000000000..7a10438de7 --- /dev/null +++ b/doc/whatsnew/fragments/7034.false_negative @@ -0,0 +1,5 @@ + +Rename ``unhashable-dict-key`` to ``unhashable-member`` and emit when creating sets and dicts, +not just when accessing dicts. + +Closes #7034, Closes #7055 diff --git a/doc/whatsnew/fragments/7153.other b/doc/whatsnew/fragments/7153.other new file mode 100644 index 0000000000..9185be078d --- /dev/null +++ b/doc/whatsnew/fragments/7153.other @@ -0,0 +1,3 @@ +Update ``astroid`` to 2.12. + +Refs #7153