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
Changes from 1 commit
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
Next Next commit
Improve blurb add command.
picnixz committed Jun 25, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit d5d11a2e71ddc55b7789655dd954f04670ccc325
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -109,12 +109,43 @@ 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 3
```

The section can be referred to from its name (case insensitive) or its ID
defined according to the following table:

| ID | Section |
|----|-------------------|
| 1 | Security |
| 2 | Core and Builtins |
| 3 | Library |
| 4 | Documentation |
| 5 | Tests |
| 6 | Build |
| 7 | Windows |
| 8 | macOS |
| 9 | IDLE |
| 10 | Tools/Demos |
| 11 | C API |

* Finally, go to the end of the file, and enter your `NEWS` entry.
This should be a single paragraph of English text using
@@ -239,6 +270,12 @@ part of the cherry-picking process.

## Changelog

### 1.2.0

- Add the `-i/--issue` and `-s/--section` options to the `add` command.
This lets you pre-fill-in the `gh-issue` and `section` fields
in the template.

### 1.1.0

- Support GitHub Issues in addition to b.p.o (bugs.python.org).
170 changes: 151 additions & 19 deletions src/blurb/blurb.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""Command-line tool to manage CPython Misc/NEWS.d entries."""
__version__ = "1.1.0"
__version__ = "1.1.1"

##
## blurb version 1.0
@@ -800,7 +800,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)
@@ -869,10 +876,112 @@ 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 section.strip('+-').isdigit():
section_index = int(section) - 1
if not (0 <= section_index < len(sections)):
sys.exit(f"Invalid section ID: {int(section)}\n\n"
f"Choose from the following table:\n\n"
f'{sections_table}')
return sections[section_index]

if not section:
sys.exit(f"Empty section name!")

section_words = section.lower().replace('_', ' ').split(' ')
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}\n\n"
f"Choose from the following table:\n\n"
f'{sections_table}')

if len(matches) > 1:
sys.exit(f"More than one match for: {raw_section}\n\n"
f"Choose from the following table:\n\n"
f'{sections_table}')

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 ID or name (case insenstitive), e.g.:

blurb add -s %(section_example_name)r
# or equivalently
blurb add -s %(section_example_id)d

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

%(sections)s
"""

editor = find_editor()
@@ -883,20 +992,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()

@@ -947,6 +1044,23 @@ def init_tmp_with_template():
print("Ready for commit.")


assert sections, 'sections is empty'
_sec_id_w = 2 + len(str(len(sections)))
_sec_name_w = 2 + max(map(len, sections))
_sec_rowrule = '+'.join(['', '-' * _sec_id_w, '-' * _sec_name_w, ''])
_format_row = ('| {:%d} | {:%d} |' % (_sec_id_w - 2, _sec_name_w - 2)).format
sections_table = '\n'.join(itertools.chain(
[_sec_rowrule, _format_row('ID', 'Section'),_sec_rowrule.replace('-', '=')],
itertools.starmap(_format_row, enumerate(sections, 1)),
[_sec_rowrule]
))
del _format_row, _sec_rowrule, _sec_name_w, _sec_id_w
add.__doc__ %= dict(
section_example_id=3,
section_example_name=sections[2],
sections=sections_table,
)


@subcommand
def release(version):
@@ -1221,25 +1335,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
@@ -1249,8 +1377,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"most be followed by an option argument")

sys.exit(fn(*filtered_args, **kwargs))
except TypeError as e:
Loading