Skip to content

refactor: convert printf-style formats to f-strings - #48

Merged
cigamit merged 1 commit into
ctrliq:mainfrom
blaipr:refactor/printf-formats
Sep 13, 2026
Merged

cigamit merged 1 commit into
ctrliq:mainfrom
blaipr:refactor/printf-formats

Conversation

@blaipr

@blaipr blaipr commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

The other half of the string formatting sweep: the 32 printf-style % formats across 12 files, which Ruff flags as UP031 and offers no safe fix for. #41 covers the str.format() calls, and the two do not overlap: this pull request touches no pre-existing .format() call and adds none.

Applied in two steps, because UP031 rewrites % to .format() rather than to an f-string, which would have left this adding calls the other pull request immediately wants to remove:

  1. ruff check --select UP031 --fix --unsafe-fixes, converting 28 of the 32.
  2. The f-string hop at those 28 sites only, six of them by hand where the message spans several lines.

Run from a throwaway Ruff configuration rather than one added to the repository, so this carries no tooling change and merges on its own. Formatted with black, which is what tox -e lint and the CI job run today.

The unsafe marking on UP031 is about one case: '%s' % x formats a tuple element-wise where '{}'.format(x) formats the tuple. Every converted site was read for that. They are assertion messages, log lines, inventory file fragments and argparse error text, and none of the substituted values can be a tuple.

Four are left as % on purpose, all in ascender/inventory.py, and Ruff is right to refuse them:

group_by_10s = 'group-%07dX.example.com' % (n / 10)

n / 10 is a float on Python 3, and %07d truncates it where {:07d} raises:

percent: 'group-0000002X.example.com'
format : ValueError: Unknown format code 'd' for object of type 'float'

So converting these is a behaviour change dressed as a style fix. They want // rather than /, which is a correctness question and belongs in its own change.

Verified with black --check, flake8 and the unit suite, 355 passing.


On the formatter. This branch is cut from main, where tox -e lint and the CI job still run black and flake8; #33 is the pull request that switches them to Ruff and is not merged. So black --check is what CI would actually run here, and validating against Ruff instead would be validating against a toolchain the base branch does not have.

Worth knowing for the rebase, though: the two formatters disagree about wrapping a long assert. Black parenthesises the condition, Ruff parenthesises the message. On main they already differ over 6 files, and the three long assertion messages converted by hand here make it 9. Nothing to fix in this branch, since #33 reformats the whole tree with ruff format and settles all of them at once, but whichever of the two lands second wants a ruff format pass as part of the rebase.

@ciq-it-service-account

ciq-it-service-account commented Sep 12, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

UP031 to reach .format(), then f-strings at those sites only, so this does not overlap the str.format conversion in ctrliq#41. Four are left as % because their argument is a float that %d truncates and {:d} rejects. Formatted with black, which is what CI runs today.
blaipr added a commit to blaipr/ascender-kit that referenced this pull request Sep 13, 2026
The last four printf-style formats in the package, left out of ctrliq#48 because Ruff refuses them and is right to.

```python
group_by_10s = 'group-%07dX.example.com' % (n / 10)
```

`n / 10` is true division, so the argument is a float. `%07d` truncates it; `{:07d}` raises `ValueError: Unknown format code 'd' for object of type 'float'`. A mechanical conversion breaks, which is why the automatic fix is unavailable.

The real problem is the division, not the format. These are group names bucketing hosts by ten, hundred and thousand, so the intent is integer division throughout, and `%d` was quietly papering over a float. Switched to `//`, and then the f-string conversion is trivial:

```python
group_by_10s = f'group-{n // 10:07d}X.example.com'
```

`hostname` came along with them: `n` is already an integer there, so it converts directly.

Checked exhaustively rather than by argument. Every `n` from 0 to 99,999, all four formats, old expression against new:

```
mismatches over 100000 hosts, 4 formats each: 0
```

That is the whole range this function is used over, since `n` comes from `range(nhosts)` and is never negative, which is the only case where truncation and floor disagree.

`json_inventory(25)` still produces the same groups and hostnames.

With this the package has no printf-style formats left: `ruff check --select UP031` reports `All checks passed!`.

Note for whoever merges: ctrliq#48 also edits this file, so whichever lands second needs a rebase.

Verified with `black --check`, `flake8` and the unit suite, 355 passing.
@blaipr
blaipr force-pushed the refactor/printf-formats branch from e11274c to 1aeade5 Compare September 13, 2026 09:03
@cigamit cigamit added the enhancement New feature or request label Sep 13, 2026
@cigamit
cigamit merged commit 935bfbe into ctrliq:main Sep 13, 2026
1 check passed
cigamit pushed a commit that referenced this pull request Sep 13, 2026
The last four printf-style formats in the package, left out of #48 because Ruff refuses them and is right to.

```python
group_by_10s = 'group-%07dX.example.com' % (n / 10)
```

`n / 10` is true division, so the argument is a float. `%07d` truncates it; `{:07d}` raises `ValueError: Unknown format code 'd' for object of type 'float'`. A mechanical conversion breaks, which is why the automatic fix is unavailable.

The real problem is the division, not the format. These are group names bucketing hosts by ten, hundred and thousand, so the intent is integer division throughout, and `%d` was quietly papering over a float. Switched to `//`, and then the f-string conversion is trivial:

```python
group_by_10s = f'group-{n // 10:07d}X.example.com'
```

`hostname` came along with them: `n` is already an integer there, so it converts directly.

Checked exhaustively rather than by argument. Every `n` from 0 to 99,999, all four formats, old expression against new:

```
mismatches over 100000 hosts, 4 formats each: 0
```

That is the whole range this function is used over, since `n` comes from `range(nhosts)` and is never negative, which is the only case where truncation and floor disagree.

`json_inventory(25)` still produces the same groups and hostnames.

With this the package has no printf-style formats left: `ruff check --select UP031` reports `All checks passed!`.

Note for whoever merges: #48 also edits this file, so whichever lands second needs a rebase.

Verified with `black --check`, `flake8` and the unit suite, 355 passing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Development

Successfully merging this pull request may close these issues.

3 participants