diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..6e895e7 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,15 @@ +target-version = "py39" + +[format] +preview = true +quote-style = "single" +docstring-code-format = true + +[lint] +preview = true +select = [ + "I", # isort +] +ignore = [ + "E501", # Ignore line length errors (we use auto-formatting) +] diff --git a/src/blurb/__main__.py b/src/blurb/__main__.py index 1813638..de13566 100644 --- a/src/blurb/__main__.py +++ b/src/blurb/__main__.py @@ -1,6 +1,6 @@ """Run blurb using ``python3 -m blurb``.""" -from blurb._cli import main +from blurb._cli import main if __name__ == '__main__': main() diff --git a/src/blurb/_add.py b/src/blurb/_add.py index 88fc97d..001086d 100644 --- a/src/blurb/_add.py +++ b/src/blurb/_add.py @@ -9,7 +9,7 @@ import tempfile from blurb._blurb_file import BlurbError, Blurbs -from blurb._cli import subcommand,error,prompt +from blurb._cli import error, prompt, subcommand from blurb._git import flush_git_add_files, git_add_files from blurb._template import sections, template @@ -43,11 +43,11 @@ def add(*, issue: str | None = None, section: str | None = None): spaces in names can be substituted for underscores: {sections} - """ + """ # fmt: skip handle, tmp_path = tempfile.mkstemp('.rst') os.close(handle) - atexit.register(lambda : os.unlink(tmp_path)) + atexit.register(lambda: os.unlink(tmp_path)) text = _blurb_template_text(issue=issue, section=section) with open(tmp_path, 'w', encoding='utf-8') as file: @@ -72,6 +72,8 @@ def add(*, issue: str | None = None, section: str | None = None): git_add_files.append(path) flush_git_add_files() print('Ready for commit.') + + add.__doc__ = add.__doc__.format(sections='\n'.join(f'* {s}' for s in sections)) @@ -178,13 +180,13 @@ def _extract_section_name(section: str | None, /) -> str | None: if not matches: section_list = '\n'.join(f'* {s}' for s in sections) - raise SystemExit(f'Invalid section name: {section!r}\n\n' - f'Valid names are:\n\n{section_list}') + raise SystemExit( + f'Invalid section name: {section!r}\n\nValid names are:\n\n{section_list}' + ) if len(matches) > 1: multiple_matches = ', '.join(f'* {m}' for m in sorted(matches)) - raise SystemExit(f'More than one match for {section!r}:\n\n' - f'{multiple_matches}') + raise SystemExit(f'More than one match for {section!r}:\n\n{multiple_matches}') return matches[0] diff --git a/src/blurb/_blurb_file.py b/src/blurb/_blurb_file.py index 4986119..b0015b9 100644 --- a/src/blurb/_blurb_file.py +++ b/src/blurb/_blurb_file.py @@ -95,8 +95,13 @@ class BlurbError(RuntimeError): class Blurbs(list): - def parse(self, text: str, *, metadata: dict[str, str] | None = None, - filename: str = 'input') -> None: + def parse( + self, + text: str, + *, + metadata: dict[str, str] | None = None, + filename: str = 'input', + ) -> None: """Parses a string. Appends a list of blurb ENTRIES to self, as tuples: (metadata, body) @@ -147,13 +152,17 @@ def finish_entry() -> None: throw(f'Invalid {issue_keys[key]} number: {value!r}') if key == 'gh-issue' and int(value) < lowest_possible_gh_issue_number: - throw(f'Invalid gh-issue number: {value!r} (must be >= {lowest_possible_gh_issue_number})') + throw( + f'Invalid gh-issue number: {value!r} (must be >= {lowest_possible_gh_issue_number})' + ) if key == 'section': if no_changes: continue if value not in sections: - throw(f'Invalid section {value!r}! You must use one of the predefined sections.') + throw( + f'Invalid section {value!r}! You must use one of the predefined sections.' + ) if 'gh-issue' not in metadata and 'bpo' not in metadata: throw("'gh-issue:' or 'bpo:' must be specified in the metadata!") @@ -232,7 +241,9 @@ def _parse_next_filename(filename: str) -> dict[str, str]: assert section in sections, f'Unknown section {section}' fields = [x.strip() for x in filename.split('.')] - assert len(fields) >= 4, f"Can't parse 'next' filename! filename {filename!r} fields {fields}" + assert len(fields) >= 4, ( + f"Can't parse 'next' filename! filename {filename!r} fields {fields}" + ) assert fields[-1] == 'rst' metadata = {'date': fields[0], 'nonce': fields[-2], 'section': section} @@ -263,7 +274,7 @@ def ensure_metadata(self) -> None: ('bpo', '0'), ('date', sortable_datetime()), ('nonce', generate_nonce(body)), - ): + ): if name not in metadata: metadata[name] = default @@ -274,10 +285,14 @@ def _extract_next_filename(self) -> str: metadata['section'] = sanitize_section(metadata['section']) metadata['root'] = root if int(metadata['gh-issue']) > 0: - path = '{root}/Misc/NEWS.d/next/{section}/{date}.gh-issue-{gh-issue}.{nonce}.rst'.format_map(metadata) + path = '{root}/Misc/NEWS.d/next/{section}/{date}.gh-issue-{gh-issue}.{nonce}.rst'.format_map( + metadata + ) elif int(metadata['bpo']) > 0: # assume it's a GH issue number - path = '{root}/Misc/NEWS.d/next/{section}/{date}.bpo-{bpo}.{nonce}.rst'.format_map(metadata) + path = '{root}/Misc/NEWS.d/next/{section}/{date}.bpo-{bpo}.{nonce}.rst'.format_map( + metadata + ) for name in ('root', 'section', 'date', 'gh-issue', 'bpo', 'nonce'): del metadata[name] return path @@ -294,4 +309,4 @@ def save_next(self) -> str: def sortable_datetime() -> str: - return time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + return time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime()) diff --git a/src/blurb/_cli.py b/src/blurb/_cli.py index f4a3f76..839c4e8 100644 --- a/src/blurb/_cli.py +++ b/src/blurb/_cli.py @@ -28,7 +28,7 @@ def prompt(prompt: str, /) -> str: def require_ok(prompt: str, /) -> str: - prompt = f"[{prompt}> " + prompt = f'[{prompt}> ' while True: s = input(prompt).strip() if s == 'ok': @@ -88,7 +88,7 @@ def help(subcommand: str | None = None) -> None: options.append(f' [-{short_option}|--{name} {metavar}]') elif p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: positionals.append(' ') - has_default = (p.default != inspect._empty) + has_default = p.default != inspect._empty if has_default: positionals.append('[') nesting += 1 @@ -146,7 +146,6 @@ def _blurb_help() -> None: def main() -> None: - args = sys.argv[1:] if not args: @@ -165,6 +164,7 @@ def main() -> None: raise SystemExit(fn(*args)) import blurb._merge + blurb._merge.original_dir = os.getcwd() try: chdir_to_repo_root() @@ -177,8 +177,7 @@ def main() -> None: kwargs = {} for name, p in inspect.signature(fn).parameters.items(): if p.kind == inspect.Parameter.KEYWORD_ONLY: - if (p.default is not None - and not isinstance(p.default, (bool, str))): + if p.default is not None and not isinstance(p.default, (bool, str)): raise SystemExit( 'blurb command-line processing cannot handle ' f'options of type {type(p.default).__qualname__}' @@ -262,7 +261,9 @@ def handle_option(s, dict): if total != 1: middle += 's' - print(f'Error: Wrong number of arguments!\n\nblurb {subcommand} {middle},\nand you specified {how_many}.') + print( + f'Error: Wrong number of arguments!\n\nblurb {subcommand} {middle},\nand you specified {how_many}.' + ) print() print('usage: ', end='') help(subcommand) @@ -293,11 +294,13 @@ def test_first_line(filename, test): return False return True - if not (test_first_line('README', readme_re) - or test_first_line('README.rst', readme_re)): + if not ( + test_first_line('README', readme_re) + or test_first_line('README.rst', readme_re) + ): continue - if not test_first_line('LICENSE', 'A. HISTORY OF THE SOFTWARE'.__eq__): + if not test_first_line('LICENSE', 'A. HISTORY OF THE SOFTWARE'.__eq__): continue if not os.path.exists('Include/Python.h'): continue @@ -307,5 +310,6 @@ def test_first_line(filename, test): break import blurb._blurb_file + blurb._blurb_file.root = path return path diff --git a/src/blurb/_merge.py b/src/blurb/_merge.py index 244d648..2ff2e4f 100644 --- a/src/blurb/_merge.py +++ b/src/blurb/_merge.py @@ -43,12 +43,14 @@ def write_news(output: str, *, versions: list[str]) -> None: def prnt(msg: str = '', /): buff.append(msg) - prnt(""" + prnt( + """ +++++++++++ Python News +++++++++++ -""".strip()) +""".strip() + ) for version in versions: filenames = glob_blurbs(version) diff --git a/src/blurb/_populate.py b/src/blurb/_populate.py index c1febc5..2e23a5c 100644 --- a/src/blurb/_populate.py +++ b/src/blurb/_populate.py @@ -1,7 +1,7 @@ import os from blurb._cli import subcommand -from blurb._git import git_add_files, flush_git_add_files +from blurb._git import flush_git_add_files, git_add_files from blurb._template import sanitize_section, sections @@ -17,7 +17,9 @@ def populate() -> None: os.makedirs(dir_path, exist_ok=True) readme_path = f'NEWS.d/next/{dir_name}/README.rst' with open(readme_path, 'w', encoding='utf-8') as readme: - readme.write(f'Put news entry ``blurb`` files for the *{section}* section in this directory.\n') + readme.write( + f'Put news entry ``blurb`` files for the *{section}* section in this directory.\n' + ) git_add_files.append(dir_path) git_add_files.append(readme_path) flush_git_add_files() diff --git a/src/blurb/_release.py b/src/blurb/_release.py index e0285d6..3da5f41 100644 --- a/src/blurb/_release.py +++ b/src/blurb/_release.py @@ -6,8 +6,12 @@ import blurb._blurb_file from blurb._blurb_file import Blurbs from blurb._cli import error, subcommand -from blurb._git import (flush_git_add_files, flush_git_rm_files, - git_rm_files, git_add_files) +from blurb._git import ( + flush_git_add_files, + flush_git_rm_files, + git_add_files, + git_rm_files, +) from blurb._utils.globs import glob_blurbs from blurb._utils.text import generate_nonce @@ -25,7 +29,9 @@ def release(version: str) -> None: existing_filenames = glob_blurbs(version) if existing_filenames: - error("Sorry, can't handle appending 'next' files to an existing version (yet).") + error( + "Sorry, can't handle appending 'next' files to an existing version (yet)." + ) output = f'Misc/NEWS.d/{version}.rst' filenames = glob_blurbs('next') @@ -35,7 +41,13 @@ def release(version: str) -> None: if not filenames: print(f'No blurbs found. Setting {version} as having no changes.') body = f'There were no new changes in version {version}.\n' - metadata = {'no changes': 'True', 'gh-issue': '0', 'section': 'Library', 'date': date, 'nonce': generate_nonce(body)} + metadata = { + 'no changes': 'True', + 'gh-issue': '0', + 'section': 'Library', + 'date': date, + 'nonce': generate_nonce(body), + } blurbs.append((metadata, body)) else: count = len(filenames) diff --git a/src/blurb/_template.py b/src/blurb/_template.py index 1b0fc9c..36429d7 100644 --- a/src/blurb/_template.py +++ b/src/blurb/_template.py @@ -52,7 +52,7 @@ 'C_API': 'C API', 'Core_and_Builtins': 'Core and Builtins', 'Tools-Demos': 'Tools/Demos', - } +} def sanitize_section(section: str, /) -> str: diff --git a/src/blurb/_utils/globs.py b/src/blurb/_utils/globs.py index c8e5519..ae06154 100644 --- a/src/blurb/_utils/globs.py +++ b/src/blurb/_utils/globs.py @@ -2,8 +2,10 @@ import os from blurb._template import ( - next_filename_unsanitize_sections, sanitize_section, - sanitize_section_legacy, sections, + next_filename_unsanitize_sections, + sanitize_section, + sanitize_section_legacy, + sections, ) diff --git a/src/blurb/_utils/text.py b/src/blurb/_utils/text.py index c2391e9..39c0399 100644 --- a/src/blurb/_utils/text.py +++ b/src/blurb/_utils/text.py @@ -48,7 +48,7 @@ def textwrap_body(body: str | Iterable[str], *, subsequent_indent: str = '') -> indents = itertools.chain( itertools.repeat(initial, 1), itertools.repeat(subsequent), - ) + ) lines = [indent + line for indent, line in zip(indents, lines)] paragraph = '\n'.join(lines) paragraphs2.append(paragraph) @@ -88,8 +88,12 @@ def textwrap_body(body: str | Iterable[str], *, subsequent_indent: str = '') -> # twice, so it's stable, and this means occasionally it'll # convert two spaces to one space, no big deal. - paragraph = '\n'.join(textwrap.wrap(paragraph.strip(), width=76, **kwargs)).rstrip() - paragraph = '\n'.join(textwrap.wrap(paragraph.strip(), width=76, **kwargs)).rstrip() + paragraph = '\n'.join( + textwrap.wrap(paragraph.strip(), width=76, **kwargs) + ).rstrip() + paragraph = '\n'.join( + textwrap.wrap(paragraph.strip(), width=76, **kwargs) + ).rstrip() paragraphs2.append(paragraph) # don't reflow literal code blocks (I hope) dont_reflow = paragraph.endswith('::') diff --git a/src/blurb/blurb.py b/src/blurb/blurb.py index 371778b..e60ac63 100755 --- a/src/blurb/blurb.py +++ b/src/blurb/blurb.py @@ -43,8 +43,8 @@ def error(*a): - s = " ".join(str(x) for x in a) - sys.exit("Error: " + s) + s = ' '.join(str(x) for x in a) + sys.exit('Error: ' + s) if __name__ == '__main__': diff --git a/tests/test_add.py b/tests/test_add.py index 3f1bf64..23eb404 100644 --- a/tests/test_add.py +++ b/tests/test_add.py @@ -3,9 +3,13 @@ import pytest import blurb._add -from blurb._add import (_blurb_template_text, _extract_issue_number, - _extract_section_name) -from blurb._template import sections as SECTIONS, template as blurb_template +from blurb._add import ( + _blurb_template_text, + _extract_issue_number, + _extract_section_name, +) +from blurb._template import sections as SECTIONS +from blurb._template import template as blurb_template def test_valid_no_issue_number(): @@ -16,26 +20,29 @@ def test_valid_no_issue_number(): assert '.. gh-issue: ' in lines -@pytest.mark.parametrize('issue', ( - # issue given by their number - '12345', - ' 12345 ', - # issue given by their number and a 'GH-' prefix - 'GH-12345', - ' GH-12345 ', - # issue given by their number and a 'gh-' prefix - 'gh-12345', - ' gh-12345 ', - # issue given by their number and a '#' prefix - '#12345', - ' #12345 ', - # issue given by their URL (no scheme) - 'github.com/python/cpython/issues/12345', - ' github.com/python/cpython/issues/12345 ', - # issue given by their URL (with scheme) - 'https://github.com/python/cpython/issues/12345', - ' https://github.com/python/cpython/issues/12345 ', -)) +@pytest.mark.parametrize( + 'issue', + ( + # issue given by their number + '12345', + ' 12345 ', + # issue given by their number and a 'GH-' prefix + 'GH-12345', + ' GH-12345 ', + # issue given by their number and a 'gh-' prefix + 'gh-12345', + ' gh-12345 ', + # issue given by their number and a '#' prefix + '#12345', + ' #12345 ', + # issue given by their URL (no scheme) + 'github.com/python/cpython/issues/12345', + ' github.com/python/cpython/issues/12345 ', + # issue given by their URL (with scheme) + 'https://github.com/python/cpython/issues/12345', + ' https://github.com/python/cpython/issues/12345 ', + ), +) def test_valid_issue_number_12345(issue): actual = _extract_issue_number(issue) assert actual == 12345 @@ -47,40 +54,46 @@ def test_valid_issue_number_12345(issue): assert '.. gh-issue: 12345' in lines -@pytest.mark.parametrize('issue', ( - '', - 'abc', - 'Gh-123', - 'gh-abc', - 'gh- 123', - 'gh -123', - 'gh-', - 'bpo-', - 'bpo-12345', - 'github.com/python/cpython/issues', - 'github.com/python/cpython/issues/', - 'github.com/python/cpython/issues/abc', - 'github.com/python/cpython/issues/gh-abc', - 'github.com/python/cpython/issues/gh-123', - 'github.com/python/cpython/issues/1234?param=1', - 'https://github.com/python/cpython/issues', - 'https://github.com/python/cpython/issues/', - 'https://github.com/python/cpython/issues/abc', - 'https://github.com/python/cpython/issues/gh-abc', - 'https://github.com/python/cpython/issues/gh-123', - 'https://github.com/python/cpython/issues/1234?param=1', -)) +@pytest.mark.parametrize( + 'issue', + ( + '', + 'abc', + 'Gh-123', + 'gh-abc', + 'gh- 123', + 'gh -123', + 'gh-', + 'bpo-', + 'bpo-12345', + 'github.com/python/cpython/issues', + 'github.com/python/cpython/issues/', + 'github.com/python/cpython/issues/abc', + 'github.com/python/cpython/issues/gh-abc', + 'github.com/python/cpython/issues/gh-123', + 'github.com/python/cpython/issues/1234?param=1', + 'https://github.com/python/cpython/issues', + 'https://github.com/python/cpython/issues/', + 'https://github.com/python/cpython/issues/abc', + 'https://github.com/python/cpython/issues/gh-abc', + 'https://github.com/python/cpython/issues/gh-123', + 'https://github.com/python/cpython/issues/1234?param=1', + ), +) def test_invalid_issue_number(issue): error_message = re.escape(f'Invalid GitHub issue number: {issue}') with pytest.raises(SystemExit, match=error_message): _blurb_template_text(issue=issue, section=None) -@pytest.mark.parametrize('invalid', ( - 'gh-issue: ', - 'gh-issue: 1', - 'gh-issue', -)) +@pytest.mark.parametrize( + 'invalid', + ( + 'gh-issue: ', + 'gh-issue: 1', + 'gh-issue', + ), +) def test_malformed_gh_issue_line(invalid, monkeypatch): template = blurb_template.replace('.. gh-issue:', invalid) error_message = re.escape("Can't find gh-issue line in the template!") @@ -120,14 +133,17 @@ def test_exact_names_lowercase(section_name, expected): _check_section_name(section_name, expected) -@pytest.mark.parametrize('section', ( - '', - ' ', - '\t', - '\n', - '\r\n', - ' ', -)) +@pytest.mark.parametrize( + 'section', + ( + '', + ' ', + '\t', + '\n', + '\r\n', + ' ', + ), +) def test_empty_section_name(section): error_message = re.escape('Empty section name!') with pytest.raises(SystemExit, match=error_message): @@ -137,26 +153,29 @@ def test_empty_section_name(section): _blurb_template_text(issue=None, section=section) -@pytest.mark.parametrize('section', [ - # Wrong capitalisation - 'C api', - 'c API', - 'LibrarY', - # Invalid - '_', - '-', - '/', - 'invalid', - 'Not a section', - # Non-special names - 'c?api', - 'cXapi', - 'C+API', - # Super-strings - 'Library and more', - 'library3', - 'librari', -]) +@pytest.mark.parametrize( + 'section', + [ + # Wrong capitalisation + 'C api', + 'c API', + 'LibrarY', + # Invalid + '_', + '-', + '/', + 'invalid', + 'Not a section', + # Non-special names + 'c?api', + 'cXapi', + 'C+API', + # Super-strings + 'Library and more', + 'library3', + 'librari', + ], +) def test_invalid_section_name(section): error_message = rf"(?m)Invalid section name: '{re.escape(section)}'\n\n.+" with pytest.raises(SystemExit, match=error_message): diff --git a/tests/test_blurb_file.py b/tests/test_blurb_file.py index b24ffed..fccfcb4 100644 --- a/tests/test_blurb_file.py +++ b/tests/test_blurb_file.py @@ -2,41 +2,41 @@ import time_machine import blurb._blurb_file -from blurb._blurb_file import Blurbs, BlurbError, sortable_datetime +from blurb._blurb_file import BlurbError, Blurbs, sortable_datetime @pytest.mark.parametrize( - "news_entry, expected_section", + 'news_entry, expected_section', ( ( - "Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst", - "Library", + 'Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst', + 'Library', ), ( - "Misc/NEWS.d/next/Core_and_Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst", - "Core and Builtins", + 'Misc/NEWS.d/next/Core_and_Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst', + 'Core and Builtins', ), ( - "Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-55555.Pf_BI7.rst", - "Core and Builtins", + 'Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-55555.Pf_BI7.rst', + 'Core and Builtins', ), ( - "Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-66666.2F1Byz.rst", - "Tools/Demos", + 'Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-66666.2F1Byz.rst', + 'Tools/Demos', ), ( - "Misc/NEWS.d/next/C_API/2023-03-27-22-09-07.gh-issue-77777.3SN8Bs.rst", - "C API", + 'Misc/NEWS.d/next/C_API/2023-03-27-22-09-07.gh-issue-77777.3SN8Bs.rst', + 'C API', ), ( - "Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-88888.3SN8Bs.rst", - "C API", + 'Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-88888.3SN8Bs.rst', + 'C API', ), ), ) def test_load_next(news_entry, expected_section, fs): # Arrange - fs.create_file(news_entry, contents="testing") + fs.create_file(news_entry, contents='testing') blurbs = Blurbs() # Act @@ -44,34 +44,34 @@ def test_load_next(news_entry, expected_section, fs): # Assert metadata = blurbs[0][0] - assert metadata["section"] == expected_section + assert metadata['section'] == expected_section @pytest.mark.parametrize( - "news_entry, expected_path", + 'news_entry, expected_path', ( ( - "Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst", - "root/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst", + 'Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst', + 'root/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst', ), ( - "Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst", - "root/Misc/NEWS.d/next/Core_and_Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst", + 'Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst', + 'root/Misc/NEWS.d/next/Core_and_Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst', ), ( - "Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-55555.2F1Byz.rst", - "root/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-55555.2F1Byz.rst", + 'Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-55555.2F1Byz.rst', + 'root/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-55555.2F1Byz.rst', ), ( - "Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst", - "root/Misc/NEWS.d/next/C_API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst", + 'Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst', + 'root/Misc/NEWS.d/next/C_API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst', ), ), ) def test_extract_next_filename(news_entry, expected_path, fs, monkeypatch): # Arrange monkeypatch.setattr(blurb._blurb_file, 'root', 'root') - fs.create_file(news_entry, contents="testing") + fs.create_file(news_entry, contents='testing') blurbs = Blurbs() blurbs.load_next(news_entry) @@ -84,7 +84,7 @@ def test_extract_next_filename(news_entry, expected_path, fs, monkeypatch): def test_parse(): # Arrange - contents = ".. gh-issue: 123456\n.. section: IDLE\nHello world!" + contents = '.. gh-issue: 123456\n.. section: IDLE\nHello world!' blurbs = Blurbs() # Act @@ -92,48 +92,48 @@ def test_parse(): # Assert metadata, body = blurbs[0] - assert metadata["gh-issue"] == "123456" - assert metadata["section"] == "IDLE" - assert body == "Hello world!\n" + assert metadata['gh-issue'] == '123456' + assert metadata['section'] == 'IDLE' + assert body == 'Hello world!\n' @pytest.mark.parametrize( - "contents, expected_error", + 'contents, expected_error', ( ( - "", + '', r"Blurb 'body' text must not be empty!", ), ( - "gh-issue: Hello world!", + 'gh-issue: Hello world!', r"Blurb 'body' can't start with 'gh-'!", ), ( - ".. gh-issue: 1\n.. section: IDLE\nHello world!", + '.. gh-issue: 1\n.. section: IDLE\nHello world!', r"Invalid gh-issue number: '1' \(must be >= 32426\)", ), ( - ".. bpo: one-two\n.. section: IDLE\nHello world!", + '.. bpo: one-two\n.. section: IDLE\nHello world!', r"Invalid bpo number: 'one-two'", ), ( - ".. gh-issue: one-two\n.. section: IDLE\nHello world!", + '.. gh-issue: one-two\n.. section: IDLE\nHello world!', r"Invalid GitHub number: 'one-two'", ), ( - ".. gh-issue: 123456\n.. section: Funky Kong\nHello world!", + '.. gh-issue: 123456\n.. section: Funky Kong\nHello world!', r"Invalid section 'Funky Kong'! You must use one of the predefined sections", ), ( - ".. gh-issue: 123456\nHello world!", + '.. gh-issue: 123456\nHello world!', r"No 'section' specified. You must provide one!", ), ( - ".. gh-issue: 123456\n.. section: IDLE\n.. section: IDLE\nHello world!", + '.. gh-issue: 123456\n.. section: IDLE\n.. section: IDLE\nHello world!', r"Blurb metadata sets 'section' twice!", ), ( - ".. section: IDLE\nHello world!", + '.. section: IDLE\nHello world!', r"'gh-issue:' or 'bpo:' must be specified in the metadata!", ), ), @@ -147,6 +147,6 @@ def test_parse_no_body(contents, expected_error): blurbs.parse(contents) -@time_machine.travel("2025-01-07 16:28:41") +@time_machine.travel('2025-01-07 16:28:41') def test_sortable_datetime(): - assert sortable_datetime() == "2025-01-07-16-28-41" + assert sortable_datetime() == '2025-01-07-16-28-41' diff --git a/tests/test_cli.py b/tests/test_cli.py index 334fc0e..d70b615 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -7,4 +7,4 @@ def test_version(capfd): # Assert captured = capfd.readouterr() - assert captured.out.startswith("blurb version ") + assert captured.out.startswith('blurb version ') diff --git a/tests/test_parser.py b/tests/test_parser.py index cc1587f..1b063e3 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -8,28 +8,28 @@ class TestParserPasses: - directory = "tests/pass" + directory = 'tests/pass' def filename_test(self, filename): b = Blurbs() b.load(filename) assert b - if os.path.exists(filename + ".res"): - with open(filename + ".res", encoding="utf-8") as file: + if os.path.exists(filename + '.res'): + with open(filename + '.res', encoding='utf-8') as file: expected = file.read() assert str(b) == expected def test_files(self): with chdir(self.directory): - for filename in glob.glob("*"): - if filename.endswith(".res"): + for filename in glob.glob('*'): + if filename.endswith('.res'): assert os.path.exists(filename[:-4]), filename continue self.filename_test(filename) class TestParserFailures(TestParserPasses): - directory = "tests/fail" + directory = 'tests/fail' def filename_test(self, filename): b = Blurbs() diff --git a/tests/test_release.py b/tests/test_release.py index 3b4d25b..3c09dcc 100644 --- a/tests/test_release.py +++ b/tests/test_release.py @@ -3,6 +3,6 @@ from blurb._release import current_date -@time_machine.travel("2025-01-07") +@time_machine.travel('2025-01-07') def test_current_date(): - assert current_date() == "2025-01-07" + assert current_date() == '2025-01-07' diff --git a/tests/test_template.py b/tests/test_template.py index 2c407a6..7ba9bb5 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -1,10 +1,9 @@ import pytest + import blurb._template from blurb._template import sanitize_section, unsanitize_section -UNCHANGED_SECTIONS = ( - "Library", -) +UNCHANGED_SECTIONS = ('Library',) def test_section_names(): @@ -23,18 +22,18 @@ def test_section_names(): ) -@pytest.mark.parametrize("section", UNCHANGED_SECTIONS) +@pytest.mark.parametrize('section', UNCHANGED_SECTIONS) def test_sanitize_section_no_change(section): sanitized = sanitize_section(section) assert sanitized == section @pytest.mark.parametrize( - "section, expected", + 'section, expected', ( - ("C API", "C_API"), - ("Core and Builtins", "Core_and_Builtins"), - ("Tools/Demos", "Tools-Demos"), + ('C API', 'C_API'), + ('Core and Builtins', 'Core_and_Builtins'), + ('Tools/Demos', 'Tools-Demos'), ), ) def test_sanitize_section_changed(section, expected): @@ -42,17 +41,15 @@ def test_sanitize_section_changed(section, expected): assert sanitized == expected -@pytest.mark.parametrize("section", UNCHANGED_SECTIONS) +@pytest.mark.parametrize('section', UNCHANGED_SECTIONS) def test_unsanitize_section_no_change(section): unsanitized = unsanitize_section(section) assert unsanitized == section @pytest.mark.parametrize( - "section, expected", - ( - ("Tools-Demos", "Tools/Demos"), - ), + 'section, expected', + (('Tools-Demos', 'Tools/Demos'),), ) def test_unsanitize_section_changed(section, expected): unsanitized = unsanitize_section(section) diff --git a/tests/test_utils_text.py b/tests/test_utils_text.py index 831a649..962792c 100644 --- a/tests/test_utils_text.py +++ b/tests/test_utils_text.py @@ -1,43 +1,44 @@ import pytest + from blurb._utils.text import textwrap_body @pytest.mark.parametrize( - "body, subsequent_indent, expected", + 'body, subsequent_indent, expected', ( ( - "This is a test of the textwrap_body function with a string. It should wrap the text to 79 characters.", - "", - "This is a test of the textwrap_body function with a string. It should wrap\n" - "the text to 79 characters.\n", + 'This is a test of the textwrap_body function with a string. It should wrap the text to 79 characters.', + '', + 'This is a test of the textwrap_body function with a string. It should wrap\n' + 'the text to 79 characters.\n', ), ( [ - "This is a test of the textwrap_body function", - "with an iterable of strings.", - "It should wrap the text to 79 characters.", + 'This is a test of the textwrap_body function', + 'with an iterable of strings.', + 'It should wrap the text to 79 characters.', ], - "", - "This is a test of the textwrap_body function with an iterable of strings. It\n" - "should wrap the text to 79 characters.\n", + '', + 'This is a test of the textwrap_body function with an iterable of strings. It\n' + 'should wrap the text to 79 characters.\n', ), ( - "This is a test of the textwrap_body function with a string and subsequent indent.", - " ", - "This is a test of the textwrap_body function with a string and subsequent\n" - " indent.\n", + 'This is a test of the textwrap_body function with a string and subsequent indent.', + ' ', + 'This is a test of the textwrap_body function with a string and subsequent\n' + ' indent.\n', ), ( - "This is a test of the textwrap_body function with a bullet list and subsequent indent. The list should not be wrapped.\n" - "\n" - "* Item 1\n" - "* Item 2\n", - " ", - "This is a test of the textwrap_body function with a bullet list and\n" - " subsequent indent. The list should not be wrapped.\n" - "\n" - " * Item 1\n" - " * Item 2\n", + 'This is a test of the textwrap_body function with a bullet list and subsequent indent. The list should not be wrapped.\n' + '\n' + '* Item 1\n' + '* Item 2\n', + ' ', + 'This is a test of the textwrap_body function with a bullet list and\n' + ' subsequent indent. The list should not be wrapped.\n' + '\n' + ' * Item 1\n' + ' * Item 2\n', ), ), ) diff --git a/tests/test_versions.py b/tests/test_versions.py index 8f34882..f625927 100644 --- a/tests/test_versions.py +++ b/tests/test_versions.py @@ -4,18 +4,18 @@ @pytest.mark.parametrize( - "version1, version2", + 'version1, version2', ( - ("2", "3"), - ("3.5.0a1", "3.5.0b1"), - ("3.5.0a1", "3.5.0rc1"), - ("3.5.0a1", "3.5.0"), - ("3.6.0b1", "3.6.0b2"), - ("3.6.0b1", "3.6.0rc1"), - ("3.6.0b1", "3.6.0"), - ("3.7.0rc1", "3.7.0rc2"), - ("3.7.0rc1", "3.7.0"), - ("3.8", "3.8.1"), + ('2', '3'), + ('3.5.0a1', '3.5.0b1'), + ('3.5.0a1', '3.5.0rc1'), + ('3.5.0a1', '3.5.0'), + ('3.6.0b1', '3.6.0b2'), + ('3.6.0b1', '3.6.0rc1'), + ('3.6.0b1', '3.6.0'), + ('3.7.0rc1', '3.7.0rc2'), + ('3.7.0rc1', '3.7.0'), + ('3.8', '3.8.1'), ), ) def test_version_key(version1, version2): @@ -30,15 +30,15 @@ def test_version_key(version1, version2): def test_glob_versions(fs): # Arrange fake_version_blurbs = ( - "Misc/NEWS.d/3.7.0.rst", - "Misc/NEWS.d/3.7.0a1.rst", - "Misc/NEWS.d/3.7.0a2.rst", - "Misc/NEWS.d/3.7.0b1.rst", - "Misc/NEWS.d/3.7.0b2.rst", - "Misc/NEWS.d/3.7.0rc1.rst", - "Misc/NEWS.d/3.7.0rc2.rst", - "Misc/NEWS.d/3.9.0b1.rst", - "Misc/NEWS.d/3.12.0a1.rst", + 'Misc/NEWS.d/3.7.0.rst', + 'Misc/NEWS.d/3.7.0a1.rst', + 'Misc/NEWS.d/3.7.0a2.rst', + 'Misc/NEWS.d/3.7.0b1.rst', + 'Misc/NEWS.d/3.7.0b2.rst', + 'Misc/NEWS.d/3.7.0rc1.rst', + 'Misc/NEWS.d/3.7.0rc2.rst', + 'Misc/NEWS.d/3.9.0b1.rst', + 'Misc/NEWS.d/3.12.0a1.rst', ) for fn in fake_version_blurbs: fs.create_file(fn) @@ -48,27 +48,27 @@ def test_glob_versions(fs): # Assert assert versions == [ - "3.12.0a1", - "3.9.0b1", - "3.7.0", - "3.7.0rc2", - "3.7.0rc1", - "3.7.0b2", - "3.7.0b1", - "3.7.0a2", - "3.7.0a1", + '3.12.0a1', + '3.9.0b1', + '3.7.0', + '3.7.0rc2', + '3.7.0rc1', + '3.7.0b2', + '3.7.0b1', + '3.7.0a2', + '3.7.0a1', ] @pytest.mark.parametrize( - "version, expected", + 'version, expected', ( - ("next", "next"), - ("3.12.0a1", "3.12.0 alpha 1"), - ("3.12.0b2", "3.12.0 beta 2"), - ("3.12.0rc2", "3.12.0 release candidate 2"), - ("3.12.0", "3.12.0 final"), - ("3.12.1", "3.12.1 final"), + ('next', 'next'), + ('3.12.0a1', '3.12.0 alpha 1'), + ('3.12.0b2', '3.12.0 beta 2'), + ('3.12.0rc2', '3.12.0 release candidate 2'), + ('3.12.0', '3.12.0 final'), + ('3.12.1', '3.12.1 final'), ), ) def test_printable_version(version, expected):