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 13 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
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
picnixz marked this conversation as resolved.
Show resolved Hide resolved
```

* 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 |
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 +255,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-in the `gh-issue` and `section` fields
picnixz marked this conversation as resolved.
Show resolved Hide resolved
in the template.

### 1.1.0

Expand Down
172 changes: 154 additions & 18 deletions src/blurb/blurb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
"""Command-line tool to manage CPython Misc/NEWS.d entries."""

##
## Part of the blurb package.
## Copyright 2015-2018 by Larry Hastings
Expand Down Expand Up @@ -799,7 +800,14 @@
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}]")

Check warning on line 804 in src/blurb/blurb.py

View check run for this annotation

Codecov / codecov/patch

src/blurb/blurb.py#L803-L804

Added lines #L803 - L804 were not covered by tests
else:
if p.default is None:
metavar = f'{name.upper()}'

Check warning on line 807 in src/blurb/blurb.py

View check run for this annotation

Codecov / codecov/patch

src/blurb/blurb.py#L806-L807

Added lines #L806 - L807 were not covered by tests
else:
metavar = f'{name.upper()}[={p.default}]'
options.append(f" [-{short_option}|--{name} {metavar}]")

Check warning on line 810 in src/blurb/blurb.py

View check run for this annotation

Codecov / codecov/patch

src/blurb/blurb.py#L809-L810

Added lines #L809 - L810 were not covered by tests
elif p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
positionals.append(" ")
has_default = (p.default != inspect._empty)
Expand Down Expand Up @@ -868,10 +876,115 @@
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!")

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}\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}\n"
f"Matches: {multiple_matches}\n\n"
f"Choose from the following table:\n\n"
picnixz marked this conversation as resolved.
Show resolved Hide resolved
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!")

Check warning on line 949 in src/blurb/blurb.py

View check run for this annotation

Codecov / codecov/patch

src/blurb/blurb.py#L949

Added line #L949 was not covered by tests
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
picnixz marked this conversation as resolved.
Show resolved Hide resolved
# 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()
Expand All @@ -882,20 +995,8 @@

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)

Check warning on line 999 in src/blurb/blurb.py

View check run for this annotation

Codecov / codecov/patch

src/blurb/blurb.py#L998-L999

Added lines #L998 - L999 were not covered by tests

init_tmp_with_template()

Expand Down Expand Up @@ -946,6 +1047,23 @@
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(
picnixz marked this conversation as resolved.
Show resolved Hide resolved
section_example_id=3,
section_example_name=sections[2],
sections=sections_table,
)


@subcommand
def release(version):
Expand Down Expand Up @@ -1220,25 +1338,39 @@
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 +1380,12 @@
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