Skip to content
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: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ target-version = "py38"
extend-exclude = ["build", ".eggs"]

[tool.ruff.lint]
# Keep ruff's default rules (E, F) and also run import sorting (I).
extend-select = ["I"]
# Keep ruff's default rules (E, F) and also run import sorting (I), modern annotation
# syntax (UP006, UP007, UP045, UP037) and the future import it needs on py38 (FA102).
extend-select = ["I", "UP006", "UP007", "UP037", "UP045", "FA102"]

[tool.ruff.lint.isort]
known-first-party = ["revup"]
Expand Down
13 changes: 7 additions & 6 deletions revup/amend.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from __future__ import annotations

import argparse
import asyncio
import logging
import re
import shlex
import subprocess
from collections import defaultdict
from typing import Dict, List, Optional, Set

from revup import git, topic_stack
from revup.core_types import (
Expand All @@ -28,7 +29,7 @@

async def invoke_editor_for_commit_msg(
git_ctx: git.Git,
editor: Optional[str],
editor: str | None,
topic_summary: str,
commit_msg: str,
cache_stat: str,
Expand Down Expand Up @@ -155,7 +156,7 @@ async def replay_cherry_pick(


async def rebuild_stack_last_touched(
git_ctx: git.Git, stack: List[CommitHeader], staged_files: Set[str]
git_ctx: git.Git, stack: list[CommitHeader], staged_files: set[str]
) -> GitCommitHash:
"""
Rebuild the stack, amending each staged file into the most recent commit that touched it.
Expand All @@ -164,7 +165,7 @@ async def rebuild_stack_last_touched(
# Map each staged file to its most recent commit in the stack (ordered oldest-first).
# -z gives NUL-terminated, unquoted paths so names with spaces, quotes, or unicode
# match staged_files (also -z) exactly rather than git's default C-quoted form.
file_to_commit: Dict[str, int] = {}
file_to_commit: dict[str, int] = {}
for i, commit_obj in enumerate(stack):
touched = await git_ctx.git_stdout(
"diff-tree",
Expand All @@ -179,7 +180,7 @@ async def rebuild_stack_last_touched(
if f and f in staged_files:
file_to_commit[f] = i

commit_to_files: Dict[int, Set[str]] = defaultdict(set)
commit_to_files: dict[int, set[str]] = defaultdict(set)
for f, idx in file_to_commit.items():
commit_to_files[idx].add(f)

Expand All @@ -190,7 +191,7 @@ async def rebuild_stack_last_touched(
# Get index entries for each staged file (format: <mode> <blob> <stage>\t<path>).
# A staged deletion has no entry here; its path stays out of staged_entries so
# the overlay omits it, which the pre-image merge base below turns into a removal.
staged_entries: Dict[str, str] = {}
staged_entries: dict[str, str] = {}
ls_output = await git_ctx.git_stdout("ls-files", "--stage", "-z", "--", *files_to_amend)
for line in ls_output.split("\0"):
if not line:
Expand Down
5 changes: 3 additions & 2 deletions revup/cherry_pick.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import argparse
import logging
from typing import Tuple

from revup import config, git
from revup.core_types import GitCommitHash, GitTreeHash, RevupUsageException
Expand All @@ -9,7 +10,7 @@

async def resolve_pr_url(
args: argparse.Namespace, git_ctx: git.Git, conf: config.Config, pr_url: str
) -> Tuple[str, str]:
) -> tuple[str, str]:
"""
Resolve a PR URL to (head_ref, base_ref) by querying the GitHub API.
"""
Expand Down
15 changes: 8 additions & 7 deletions revup/completion.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

import argparse
import asyncio
import enum
import logging
import os
from pathlib import Path
from typing import List, Optional

from revup.config import Config, RevupArgParser

Expand Down Expand Up @@ -41,11 +42,11 @@ def default_rc_file(self) -> str:
}


def topic_completer(prefix: str, parsed_args: argparse.Namespace, **_kwargs: object) -> List[str]:
def topic_completer(prefix: str, parsed_args: argparse.Namespace, **_kwargs: object) -> list[str]:
try:
from revup import git, toolkit

async def _get_names() -> List[str]:
async def _get_names() -> list[str]:
# Build git and topics exactly as `revup toolkit list-topics` does, so
# completion resolves the same base branch and returns the same topics.
git_ctx = await git.make_git(parsed_args)
Expand All @@ -68,9 +69,9 @@ async def _get_names() -> List[str]:


def make_config_flag_completer(
all_parsers: List[RevupArgParser],
all_parsers: list[RevupArgParser],
) -> object:
def _completer(prefix: str, **_kwargs: object) -> List[str]:
def _completer(prefix: str, **_kwargs: object) -> list[str]:
from revup.config import collect_known_keys

flags = []
Expand All @@ -90,7 +91,7 @@ def prompt_config_key(shell: ShellType) -> str:


def install_completion(
shell: ShellType, rc_file: Optional[str] = None, conf: Optional[Config] = None
shell: ShellType, rc_file: str | None = None, conf: Config | None = None
) -> int:
rc_path = Path(rc_file or shell.default_rc_file).expanduser()
source_line = SOURCE_LINE_TEMPLATES[shell]
Expand All @@ -112,7 +113,7 @@ def install_completion(
return 0


def detect_default_shell() -> Optional[ShellType]:
def detect_default_shell() -> ShellType | None:
shell_env = os.environ.get("SHELL", "")
basename = Path(shell_env).name
for s in ShellType:
Expand Down
20 changes: 11 additions & 9 deletions revup/config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from __future__ import annotations

import argparse
import configparser
import getpass
import logging
import os
import re
from argparse import _StoreAction, _StoreFalseAction, _StoreTrueAction
from typing import Any, Dict, List, Optional, Tuple
from typing import Any

from revup.core_types import RevupUsageException

Expand Down Expand Up @@ -46,7 +48,7 @@ def add_argument(self, *args: Any, **kwargs: Any) -> argparse.Action:

return action

def collect_excluded_completions(self) -> List[str]:
def collect_excluded_completions(self) -> list[str]:
excluded = []
for action in self._actions:
completable = getattr(action, "completable", False)
Expand All @@ -68,8 +70,8 @@ def set_defaults_from_config(self, conf: configparser.ConfigParser) -> None:
def get_command(self) -> str:
return self.prog.split()[-1].replace("-", "_")

def get_actions(self) -> Dict[str, argparse.Action]:
ret: Dict[str, argparse.Action] = {}
def get_actions(self) -> dict[str, argparse.Action]:
ret: dict[str, argparse.Action] = {}
for action in self._actions:
if not isinstance(action, (_StoreTrueAction, _StoreAction)):
# Ignore nonconfigurable actions (help, auto-generated negation)
Expand Down Expand Up @@ -132,7 +134,7 @@ def __init__(
self.config_path = config_path
self.repo_config_path = repo_config_path
self.git_dir_config_path = git_dir_config_path
self.file_configs: List[Tuple[str, configparser.ConfigParser]] = []
self.file_configs: list[tuple[str, configparser.ConfigParser]] = []

def read(self) -> None:
# Read in increasing precedence: later reads overwrite earlier ones.
Expand All @@ -156,7 +158,7 @@ def write(self) -> None:
self.config.write(f)
self.dirty = False

def set_value(self, section: str, key: str, value: Optional[str]) -> None:
def set_value(self, section: str, key: str, value: str | None) -> None:
if value is None:
if self.config.has_option(section, key):
self.config.remove_option(section, key)
Expand All @@ -174,7 +176,7 @@ def set_value(self, section: str, key: str, value: Optional[str]) -> None:
def get_config(self) -> configparser.ConfigParser:
return self.config

def apply_to_parsers(self, all_parsers: List[RevupArgParser]) -> None:
def apply_to_parsers(self, all_parsers: list[RevupArgParser]) -> None:
for p in all_parsers:
p.set_defaults_from_config(self.config)
known = collect_known_keys(all_parsers)
Expand All @@ -189,11 +191,11 @@ def apply_to_parsers(self, all_parsers: List[RevupArgParser]) -> None:
logging.warning(f"Unrecognized config key {section}.{key} in {path}")


def collect_known_keys(all_parsers: List[RevupArgParser]) -> Dict[str, List[str]]:
def collect_known_keys(all_parsers: list[RevupArgParser]) -> dict[str, list[str]]:
return {p.get_command(): list(p.get_actions().keys()) for p in all_parsers}


def config_main(conf: Config, args: argparse.Namespace, all_parsers: List[RevupArgParser]) -> int:
def config_main(conf: Config, args: argparse.Namespace, all_parsers: list[RevupArgParser]) -> int:
split_key = args.flag[0].replace("-", "_").split(".")
if len(split_key) == 1:
command = "revup"
Expand Down
14 changes: 8 additions & 6 deletions revup/core_types.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import Dict, List, NewType
from typing import NewType

# A bunch of commonly used type definitions.

Expand All @@ -17,7 +19,7 @@ class CommitHeader:
"""

tree: GitTreeHash
parents: List[GitCommitHash]
parents: list[GitCommitHash]
author_name: str = ""
author_email: str = ""
author_date: str = ""
Expand All @@ -33,15 +35,15 @@ class CommitHeader:
class GitConflict:
type: str
message: str
paths: List[str]
paths: list[str]


# A conflict has appeared while doing a git operation. The higher level command
# will catch this so it can either handle it or re-raise.
class GitConflictException(Exception):
def __init__(self, tree: GitTreeHash):
self.tree = tree
self.conflicts: List[GitConflict] = []
self.conflicts: list[GitConflict] = []


# Incorrect arguments or other usage error.
Expand Down Expand Up @@ -70,7 +72,7 @@ class RevupShellException(Exception):


class RevupForgeException(Exception):
def __init__(self, error_json: List[Dict]):
def __init__(self, error_json: list[dict]):
super().__init__()
self.error_json = error_json
messages = []
Expand All @@ -84,7 +86,7 @@ def __init__(self, error_json: List[Dict]):


class RevupRequestException(Exception):
def __init__(self, status: int, response: Dict):
def __init__(self, status: int, response: dict):
super().__init__()
self.status = status
self.response = response
Loading
Loading