Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-6: add -i/--issue and -s/--section flags to blurb add #16

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d5d11a2
Improve `blurb add` command.
picnixz Jun 25, 2024
eaddee3
update version
picnixz Jun 25, 2024
403d387
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 25, 2024
ad230fe
fix CI/CD
picnixz Jun 25, 2024
edd8eea
Merge branch 'add-gh-flags' of github.com:picnixz/blurb into add-gh-f…
picnixz Jun 25, 2024
9856548
fixup! typos
picnixz Jun 26, 2024
a2a1fce
add test for known section names
picnixz Jun 26, 2024
7d67830
expand tests
picnixz Jun 26, 2024
635c8ac
improve section name detection
picnixz Jun 26, 2024
992b8ec
improve tests
picnixz Jun 26, 2024
842bc2d
address review!
picnixz Jun 26, 2024
99261a5
remove extraneous line
picnixz Jun 26, 2024
114013c
Merge branch 'main' into add-gh-flags
picnixz Jul 12, 2024
18a5563
address Larry's comments
picnixz Jul 13, 2024
350daeb
remove local fix
picnixz Jul 13, 2024
0e25cc0
remove un-necessary blank line
picnixz Jul 13, 2024
2b976e2
...
picnixz Jul 13, 2024
d578f92
use the same example in the README and the docstring of `add`
picnixz Jul 13, 2024
f11b9f1
Update README.md
picnixz Jul 13, 2024
6737ea4
Update README.md
picnixz Jul 13, 2024
384079d
Update src/blurb/blurb.py
picnixz Jul 13, 2024
9242ddc
Update src/blurb/blurb.py
hugovk Jul 13, 2024
8619bc2
Update README.md
picnixz Jul 13, 2024
9de55af
improve matching algorithm
picnixz Jul 13, 2024
a7cd263
increase test coverage
picnixz Jul 13, 2024
ba33c38
update comments
picnixz Jul 13, 2024
cb04947
simplify supported separators
picnixz Jul 13, 2024
33ae76e
fix regex
picnixz Jul 13, 2024
48fc24b
improve error messages
picnixz Jul 13, 2024
15271e1
cleanup
picnixz Jul 13, 2024
a3899ec
Merge branch 'main' into add-gh-flags
picnixz Aug 14, 2024
026052c
Update README.md
picnixz Aug 14, 2024
5fe40bd
Update CHANGELOG.md
picnixz Aug 14, 2024
1f789b0
Merge branch 'main' into add-gh-flags
picnixz Nov 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,30 @@ The template for the `blurb add` message looks like this:
Here's how you interact with the file:

* Add the GitHub issue number for this commit to the
end of the `.. gh-issue:` line.
end of the `.. gh-issue:` line. The issue can also
be specified via the ``-i/--issue`` option:

```shell
$ blurb add -i 109198
# or equivalently
$ blurb add -i https://github.com/python/cpython/issues/109198
```

* Uncomment the line with the relevant `Misc/NEWS` section for this entry.
For example, if this should go in the `Library` section, uncomment
the line reading `#.. section: Library`. To uncomment, just delete
the `#` at the front of the line.
the `#` at the front of the line. Alternatively, the section can
be specified via the ``-s/--section`` option:

```shell
$ blurb add -s 'Library'
# or equivalently
$ blurb add -s lib
```

The known section names are given in the following table. The match
is performed casse insensitively and partial matching is supported
as long as the match is unique.
picnixz marked this conversation as resolved.
Show resolved Hide resolved

* Finally, go to the end of the file, and enter your `NEWS` entry.
This should be a single paragraph of English text using
Expand Down Expand Up @@ -224,6 +242,9 @@ part of the cherry-picking process.
- Loosen README check for CPython forks.
- Move code from `python/core-workflow` to own `python/blurb` repo.
- Deploy to PyPI via Trusted Publishers.
- Add the `-i/--issue` and `-s/--section` options to the `add` command.
This lets you pre-fill the `gh-issue` and `section` fields.
in the template.

### 1.1.0

Expand Down
160 changes: 142 additions & 18 deletions src/blurb/blurb.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@
# This template is the canonical list of acceptable section names!
# It's parsed internally into the "sections" set.
#
# Do not forget to update the example for the 'add' command if
# the section names change as well as the corresponding tests.
#

template = """

Expand Down Expand Up @@ -799,7 +802,14 @@ def help(subcommand=None):
for name, p in inspect.signature(fn).parameters.items():
if p.kind == inspect.Parameter.KEYWORD_ONLY:
short_option = name[0]
options.append(f" [-{short_option}|--{name}]")
if isinstance(p.default, bool):
options.append(f" [-{short_option}|--{name}]")
else:
if p.default is None:
metavar = f'{name.upper()}'
else:
metavar = f'{name.upper()}[={p.default}]'
options.append(f" [-{short_option}|--{name} {metavar}]")
elif p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
positionals.append(" ")
has_default = (p.default != inspect._empty)
Expand Down Expand Up @@ -868,10 +878,107 @@ def find_editor():
error('Could not find an editor! Set the EDITOR environment variable.')


def _extract_issue_number(issue):
if issue is None:
return None

issue = raw_issue = str(issue).strip()
if issue.startswith('gh-'):
issue = issue[3:]
if issue.isdigit():
return issue

match = re.match(r'^(?:https://)?github\.com/python/cpython/issues/(\d+)$', issue)
if match is None:
sys.exit(f"Invalid GitHub issue: {raw_issue}")
return match.group(1)


def _extract_section_name(section):
if section is None:
return None

section = raw_section = section.strip()
if not section:
sys.exit("Empty section name!")

sanitized = re.sub(r'[_-]', ' ', section)
section_words = re.split(r'\s+', sanitized)
section_pattern = '[_ ]'.join(map(re.escape, section_words))
section_re = re.compile(section_pattern, re.I)

matches = []
for section_name in sections:
if section_re.match(section_name):
matches.append(section_name)

if not matches:
sys.exit(f"Invalid section name: {raw_section!r}\n\n"
f"Choose from the following table:\n\n"
f'{sections_table}')

if len(matches) > 1:
multiple_matches = ', '.join(map(repr, sorted(matches)))
sys.exit(f"More than one match for: {raw_section!r}\n"
f"Matches: {multiple_matches}")

return matches[0]


def _update_blurb_template(issue, section):
issue_number = _extract_issue_number(issue)
section_name = _extract_section_name(section)

# Ensure that a whitespace is given after 'gh-issue:'
# to help filling up the template, unless an issue
# number was manually specified via the CLI.
text = template

issue_line = ".. gh-issue:"
pattern = "\n" + issue_line + "\n"
if issue_number is None:
if pattern not in text:
sys.exit("Can't find gh-issue line to ensure there's a space on the end!")
replacement = "\n" + issue_line + " \n"
else:
if pattern not in text:
sys.exit("Can't find gh-issue line to fill!")
replacement = "\n" + issue_line + " " + str(issue_number) + "\n"

text = text.replace(pattern, replacement)

# Uncomment the section if needed.
if section_name is not None:
pattern = f'#.. section: {section_name}'
text = text.replace(pattern, pattern.lstrip('#'))

return text


@subcommand
def add():
def add(*, issue=None, section=None):
"""
Add a blurb (a Misc/NEWS.d/next entry) to the current CPython repo.

Use -i/--issue to specify a GitHub issue number or link, e.g.:

blurb add -i 109198

# or equivalently
blurb add -i https://github.com/python/cpython/issues/109198

The blurb's section can be specified via -s/--section
with its name (case insenstitive), e.g.:

blurb add -s 'Library'

# or using a partial matching
blurb add -s lib

The known sections names are defined as follows and
spaces in names can be substituted for underscores:

{sections}
"""

editor = find_editor()
Expand All @@ -882,20 +989,8 @@ def add():

def init_tmp_with_template():
with open(tmp_path, "wt", encoding="utf-8") as file:
# hack:
# my editor likes to strip trailing whitespace from lines.
# normally this is a good idea. but in the case of the template
# it's unhelpful.
# so, manually ensure there's a space at the end of the gh-issue line.
text = template

issue_line = ".. gh-issue:"
without_space = "\n" + issue_line + "\n"
with_space = "\n" + issue_line + " \n"
if without_space not in text:
sys.exit("Can't find gh-issue line to ensure there's a space on the end!")
text = text.replace(without_space, with_space)
file.write(text)
updated = _update_blurb_template(issue, section)
file.write(updated)

init_tmp_with_template()

Expand Down Expand Up @@ -946,6 +1041,17 @@ def init_tmp_with_template():
print("Ready for commit.")


assert sections, 'sections is empty'
_sec_name_width = 2 + max(map(len, sections))
_sec_row_rule = '+'.join(['', '-' * _sec_name_width, ''])
_format_row = (f'| {{:{_sec_name_width - 2:d}}} |').format
del _sec_name_width
sections_table = '\n'.join(map(_format_row, sections))
del _format_row
sections_table = '\n'.join((_sec_row_rule, sections_table, _sec_rowrule))
hugovk marked this conversation as resolved.
Show resolved Hide resolved
del _sec_row_rule
add.__doc__ = add.__doc__.format(sections=sections_table)


@subcommand
def release(version):
Expand Down Expand Up @@ -1220,25 +1326,39 @@ def main():
kwargs = {}
for name, p in inspect.signature(fn).parameters.items():
if p.kind == inspect.Parameter.KEYWORD_ONLY:
assert isinstance(p.default, bool), "blurb command-line processing only handles boolean options"
assert p.default is None or isinstance(p.default, (bool, str)), \
"blurb command-line processing only handles boolean options"
kwargs[name] = p.default
short_options[name[0]] = name
long_options[name] = name

filtered_args = []
done_with_options = False
consume_after = None

def handle_option(s, dict):
nonlocal consume_after
name = dict.get(s, None)
if not name:
sys.exit(f'blurb: Unknown option for {subcommand}: "{s}"')
kwargs[name] = not kwargs[name]

value = kwargs[name]
if isinstance(value, bool):
kwargs[name] = not value
else:
consume_after = name

# print(f"short_options {short_options} long_options {long_options}")
for a in args:
if consume_after:
kwargs[consume_after] = a
consume_after = None
continue

if done_with_options:
filtered_args.append(a)
continue

if a.startswith('-'):
if a == "--":
done_with_options = True
Expand All @@ -1248,8 +1368,12 @@ def handle_option(s, dict):
for s in a[1:]:
handle_option(s, short_options)
continue

filtered_args.append(a)

if consume_after:
sys.exit(f"Error: blurb: {subcommand} {consume_after} "
f"must be followed by an option argument")

sys.exit(fn(*filtered_args, **kwargs))
except TypeError as e:
Expand Down
9 changes: 8 additions & 1 deletion tests/test_blurb.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import pytest
from pyfakefs.fake_filesystem import FakeFilesystem

from blurb import blurb


def test_section_names():
assert tuple(blurb.sections) == (
'Security', 'Core and Builtins', 'Library', 'Documentation',
'Tests', 'Build', 'Windows', 'macOS', 'IDLE', 'Tools/Demos',
'C API',
)


UNCHANGED_SECTIONS = (
"Library",
)
Expand Down
Loading
Loading