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

Refactor to put API at the center #28

Merged
merged 1 commit into from
Jun 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions changelog.d/20210620_190043_nedbat_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Added
.....

- A new poorly documented API is available. See the Scriv, Changelog, and
Fragment classes in the scriv.scriv module.
142 changes: 16 additions & 126 deletions src/scriv/collect.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,17 @@
"""Collecting fragments."""

import collections
import datetime
import itertools
import logging
from pathlib import Path
from typing import Dict, Iterable, List, Optional, Sequence, Tuple, TypeVar
from typing import Optional

import click
import click_log
import jinja2

from .config import Config
from .format import SectionDict, get_format_tools
from .gitinfo import git_add, git_config_bool, git_edit, git_rm
from .scriv import Scriv

logger = logging.getLogger()


def files_to_combine(config: Config) -> List[Path]:
"""
Find all the files to be combined.

The files are returned in the order they should be processed.

"""
return sorted(
itertools.chain.from_iterable(
[
Path(config.fragment_directory).glob(pattern)
for pattern in ["*.rst", "*.md"]
]
)
)


def sections_from_file(config: Config, filename: Path) -> SectionDict:
"""
Collect the sections from a file.
"""
format_tools = get_format_tools(filename.suffix.lstrip("."), config)
text = filename.read_text().rstrip()
file_sections = format_tools.parse_text(text)
return file_sections


def combine_sections(config: Config, files: Iterable[Path]) -> SectionDict:
"""
Read files, and produce a combined SectionDict of their contents.
"""
sections = collections.defaultdict(list) # type: SectionDict
for file in files:
file_sections = sections_from_file(config, file)
for section, paragraphs in file_sections.items():
sections[section].extend(paragraphs)
return sections


T = TypeVar("T")
K = TypeVar("K")


def order_dict(
d: Dict[Optional[K], T], keys: Sequence[Optional[K]]
) -> Dict[Optional[K], T]:
"""
Produce an OrderedDict of `d`, but with the keys in `keys` order.
"""
with_order = collections.OrderedDict()
to_insert = set(d)
for k in keys:
if k not in to_insert:
continue
with_order[k] = d[k]
to_insert.remove(k)

for k in to_insert:
with_order[k] = d[k]

return with_order


def cut_at_line(text: str, marker: str) -> Tuple[str, str]:
"""
Split text into two parts: up to the line with marker, and lines after.

If `marker` isn't in the text, return ("", text)
"""
lines = text.splitlines(keepends=True)
for i, line in enumerate(lines):
if marker in line:
return "".join(lines[: i + 1]), "".join(lines[i + 1 :])
return ("", text)


@click.command()
@click.option(
"--add/--no-add", default=None, help="'git add' the updated changelog file."
Expand Down Expand Up @@ -121,55 +39,27 @@ def collect(
if edit is None:
edit = git_config_bool("scriv.collect.edit")

config = Config.read()
logger.info("Collecting from {}".format(config.fragment_directory))
files = files_to_combine(config)
sections = combine_sections(config, files)
sections = order_dict(sections, [None] + config.categories)
scriv = Scriv()
logger.info("Collecting from {}".format(scriv.config.fragment_directory))
frags = scriv.fragments_to_combine()

changelog = Path(config.output_file)
newline = ""
if changelog.exists():
with changelog.open("r") as f:
changelog_text = f.read()
if f.newlines: # .newlines may be None, str, or tuple
if isinstance(f.newlines, str):
newline = f.newlines
else:
newline = f.newlines[0]
text_before, text_after = cut_at_line(
changelog_text, config.insert_marker
)
else:
text_before = ""
text_after = ""
changelog = scriv.changelog()
changelog.read()

format_tools = get_format_tools(config.format, config)
title_data = {
"date": datetime.datetime.now(),
"version": version or config.version,
}
new_title = jinja2.Template(config.entry_title_template).render(
config=config, **title_data
)
if new_title.strip():
new_header = format_tools.format_header(new_title)
else:
new_header = ""
new_text = format_tools.format_sections(sections)
with changelog.open("w", newline=newline or None) as f:
f.write(text_before + new_header + new_text + text_after)
new_header = changelog.entry_header(version=version)
new_text = changelog.entry_text(scriv.combine_fragments(frags))
changelog.write(new_header, new_text)

if edit:
git_edit(changelog)
git_edit(changelog.path)

if add:
git_add(changelog)
git_add(changelog.path)

if not keep:
for file in files:
logger.info("Deleting fragment file {}".format(file))
for frag in frags:
logger.info("Deleting fragment file {!r}".format(str(frag.path)))
if add:
git_rm(file)
git_rm(frag.path)
else:
file.unlink()
frag.path.unlink()
53 changes: 9 additions & 44 deletions src/scriv/create.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,18 @@
"""Creating fragments."""

import datetime
import logging
import re
import sys
import textwrap
from pathlib import Path
from typing import Optional

import click
import click_log
import jinja2

from .collect import sections_from_file
from .config import Config
from .gitinfo import (
current_branch_name,
git_add,
git_config_bool,
git_edit,
user_nick,
)
from .gitinfo import git_add, git_config_bool, git_edit
from .scriv import Scriv

logger = logging.getLogger()


def new_fragment_path(config: Config) -> Path:
"""
Return the file path for a new fragment.
"""
file_name = "{:%Y%m%d_%H%M%S}_{}".format(
datetime.datetime.now(), user_nick()
)
branch_name = current_branch_name()
if branch_name and branch_name not in config.main_branches:
branch_name = branch_name.rpartition("/")[-1]
branch_name = re.sub(r"[^a-zA-Z0-9_]", "_", branch_name)
file_name += "_{}".format(branch_name)
file_name += ".{}".format(config.format)
file_path = Path(config.fragment_directory) / file_name
return file_path


def new_fragment_contents(config: Config) -> str:
"""Produce the initial contents of a scriv fragment."""
return jinja2.Template(
textwrap.dedent(config.new_fragment_template)
).render(config=config)


@click.command()
@click.option(
"--add/--no-add", default=None, help="'git add' the created file."
Expand All @@ -68,24 +32,25 @@ def create(add: Optional[bool], edit: Optional[bool]) -> None:
if edit is None:
edit = git_config_bool("scriv.create.edit")

config = Config.read()
if not Path(config.fragment_directory).exists():
scriv = Scriv()
frag = scriv.new_fragment()
file_path = frag.path
if not file_path.parent.exists():
sys.exit(
"Output directory {!r} doesn't exist, please create it.".format(
config.fragment_directory
str(file_path.parent)
)
)

file_path = new_fragment_path(config)
if file_path.exists():
sys.exit("File {} already exists, not overwriting".format(file_path))

logger.info("Creating {}".format(file_path))
file_path.write_text(new_fragment_contents(config))
frag.write()

if edit:
git_edit(file_path)
sections = sections_from_file(config, file_path)
sections = scriv.sections_from_fragment(frag)
if not sections:
logger.info("Empty fragment, aborting...")
file_path.unlink()
Expand Down
Loading