Skip to content

refactor!: rename the import package to targetprocess_py - #18

Merged
man8 merged 3 commits into
mainfrom
rename-import-package
Sep 20, 2026
Merged

man8 merged 3 commits into
mainfrom
rename-import-package

Conversation

@man8-octoflow

@man8-octoflow man8-octoflow Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Renames the import package from targetprocess to targetprocess_py, the PEP 503
normalisation of the distribution name, and ships it as 0.3.0. The distribution
stays targetprocess-py on PyPI and the repository keeps its name; only the import
name changes.

The import name did not match the distribution name, so every tool that infers a
dependency from a top-level import asked PyPI for a package called targetprocess,
which does not exist. Such a consumer needed a hand-written metadata override to
install this library at all. With the import spelt targetprocess_py the inference
resolves the real distribution unaided.

There is no compatibility shim, deliberately: a targetprocess module
re-exporting the new package would keep alive exactly the import that cannot be
resolved, and would let adopters postpone the migration indefinitely. The library is
0.x, where a breaking change is acceptable, and this is much cheaper now than after
1.0.0.

No GitHub issue; this change is tracked outside this repository.

Changes

Three commits. The order is deliberate — see The logger rename is separable below.

1. refactor!: rename the import package to targetprocess_py

  • src/targetprocess/ → src/targetprocess_py/ (56 files, py.typed included).
  • Every module reference follows it: 528 import targetprocess / from targetprocess… lines across 152 files, the :mod: / :class: / :func:
    docstring references, the coverage target in CI and CONTRIBUTING.md, the
    built-wheel smoke import in CI, and the package paths in pyproject.toml
    (hatch version path, wheel packages, the N802 per-file ignore),
    .coderabbit.yaml's path instruction and CODEOWNERS' commented example.
  • Two non-mechanical cases worth a reviewer's eye:
    tests/test_entity_extras.py discovers the entity tree by
    subclass.__module__.startswith("targetprocess."), a runtime string test
    against the package name rather than an import; and the N802 per-file ignore
    in pyproject.toml, which is the reference a sweep would most plausibly miss.
    Both are covered by the mutation tests below.

2. chore(release): prepare 0.3.0

__version__ 0.2.1 → 0.3.0; the ## [0.3.0] - 2026-09-20 heading absorbs the
entries that were under [Unreleased] and adds the import-rename entry above them;
link references updated; SECURITY.md's supported-versions table moves with the
minor, as CONTRIBUTING.md § Releasing requires, and to the state that file's own
policy prescribes (0.3.x supported, 0.2.x not).

3. refactor!: rename the logger namespace to targetprocess_py

Python's convention is that a library's root logger carries its top-level package
name — what logging.getLogger(__name__) yields. After commit 1 that name is
targetprocess_py, so getLogger("targetprocess") named something no module in
this distribution produces: a consumer configuring the logger they would derive
from their own import got nothing, while the library logged to a namespace they
never referenced.

It carries its own CHANGELOG entry, with its own migration line, because it fails
differently from the import rename: an import that no longer resolves raises at
startup, while logging configuration that no longer matches simply stops producing
output, with nothing raised and nothing logged to say so.

The logger rename is separable — and the CHANGELOG entry goes with it

The namespace rename is a judgement that can be dropped without touching the
package rename, so it must be revertable in one command including its
documentation
. A revert that left the entry standing would tell a consumer to
rename logging configuration that had not moved — the silent-logging failure the
entry itself warns about, and nothing in this repository would detect it.

That is why the logger commit is last and carries the CHANGELOG entry for its
own change. Verified rather than asserted:

$ git revert <logger commit>
[... CLEAN, no conflict]
 CHANGELOG.md                           |  8 ------
 SPEC.md                                |  8 +++---
 src/targetprocess_py/_observability.py | 20 +++++++-------
 tests/test_observability.py            | 48 ++++++++++++++++------------------
 4 files changed, 37 insertions(+), 47 deletions(-)

The reverted tree keeps the import-rename entry and __version__ = "0.3.0", loses
the logger entry entirely, and is green: ruff check clean, ruff format --check
clean, mypy --strict src clean, 1450 passed, 99.23%.

Two other commit orders were tried and rejected: with the logger commit second and
its entry under [Unreleased], the release commit relocates those lines and the
revert conflicts; with the entry in the release commit, the revert is clean but
silently leaves the entry behind.

Deliberately unchanged

The token appears in five distinct roles in this tree and only two of them are the
package. Left alone:

  • the distribution name targetprocess-py, everywhere;
  • the credential directory ~/.config/targetprocess/.env — a canonical on-disk
    path shared with tooling outside this repository, whose rename would break
    credential resolution for every consumer of it while passing every test here
    (9 occurrences, verified byte-identical to the base revision);
  • the vendor's own domains and the IBM documentation URL. These are load-bearing,
    not merely cosmetic: targetprocess.com appears inside the cassette-sanitiser
    regexes (tests/integration/conftest.py:126,
    tests/test_cassette_guard.py:121), so renaming it would quietly weaken
    sanitisation of recorded traffic;
  • prose and identifiers naming the product rather than this package: the PyPI
    search keyword (the term a user actually types to find this library), the
    correlation-ID ContextVar label, and two test names that read against
    TargetProcess's own hierarchy and error family;
  • CHANGELOG.md's historical entry for 0.1.0, which describes what shipped then.

A note on the recorded cassettes

The acceptance criteria this change was written against list the cassettes among
the files whose imports need updating. They carry none — a case-insensitive
search over all 23 files under tests/integration/cassettes/ returns nothing, with
positive controls (tpondemand: 23/23 files; UserStor: 17) confirming the search
does read those files, and with -a and default modes returning identical counts so
nothing was skipped as binary. The distribution name a cassette might carry is
unchanged. No cassette was touched, and none needed to be.

Testing

Run against the head commit:

Gate Result
uv run ruff check . All checks passed
uv run ruff format --check . 168 files already formatted
uv run mypy --strict src no issues in 55 source files
uv run pytest -q --cov=targetprocess_py --cov-fail-under=90 1450 passed, 99.23%
uv run pre-commit run --all-files --hook-stage pre-commit all hooks passed
uv run pre-commit run --all-files --hook-stage pre-push all hooks passed
uv build + uvx twine check --strict dist/* both artefacts PASSED

A green suite is real evidence here rather than ceremony: a rename that misses one
occurrence fails at import time.

The renames are load-bearing

Four mutation tests, each restoring the actual base-revision value rather than
deleting part of the new code, so each measures the real counterfactual:

Mutation (→ base value) Result
per-file-ignores path → src/targetprocess/_base.py ruff check → 6 N802 errors
__module__.startswith("targetprocess.") test_all_entity_types_list_is_complete fails
--cov=targetprocess coverage 0.00%, --cov-fail-under=90 trips
source logger name → targetprocess, tests left new 5 observability tests fail

None of the renamed assertions has been weakened into a tautology.

The built artefacts, read from the artefacts themselves

$ unzip -l dist/targetprocess_py-0.3.0-py3-none-any.whl | awk '{print $4}' \
    | grep -E '^[^/]+/' | cut -d/ -f1 | sort -u
targetprocess_py
targetprocess_py-0.3.0.dist-info

$ unzip -p dist/targetprocess_py-0.3.0-py3-none-any.whl \
    targetprocess_py-0.3.0.dist-info/METADATA | head -3
Metadata-Version: 2.5
Name: targetprocess-py
Version: 0.3.0

$ tar tzf dist/targetprocess_py-0.3.0.tar.gz | grep -E '/src/' \
    | sed 's|.*/src/||' | cut -d/ -f1 | sort -u
targetprocess_py

$ tar xzOf dist/targetprocess_py-0.3.0.tar.gz targetprocess_py-0.3.0/PKG-INFO | head -3
Metadata-Version: 2.5
Name: targetprocess-py
Version: 0.3.0

Neither artefact contains a bare targetprocess/ path; py.typed ships inside
targetprocess_py/.

The import checks, in a clean virtualenv

The virtualenv was created outside the worktree and outside any directory
containing src/, only the built wheel was installed into it, and the checks ran
from an empty directory, so import targetprocess could not resolve from the
working directory.

A positive control first, because the negative assertion is the load-bearing
one and an assertion that cannot fail is not evidence. With a throwaway
targetprocess/ package in the working directory, the very same interpreter
invocation finds it:

$ cd .../control-cwd && python -c 'import targetprocess; print(targetprocess.__file__)'
/private/tmp/.../control-cwd/targetprocess/__init__.py

So the check can see a reachable bare targetprocess. Removing it and running from
the empty directory:

$ cd .../empty-cwd && ls -A | wc -l
0
$ python -c 'import targetprocess_py, sys; print(targetprocess_py.__version__); print(targetprocess_py.__file__)'
0.3.0
/tmp/.../venv/lib/python3.13/site-packages/targetprocess_py/__init__.py

$ python -c 'import targetprocess'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import targetprocess
ModuleNotFoundError: No module named 'targetprocess'

sys.path during those checks was ['', <stdlib>, <venv site-packages>] — nothing
from this repository, and '' (the empty working directory) present, which is what
makes the negative result meaningful rather than an artefact of an isolated
interpreter.

The public surface is unchanged

__all__ carries 107 names both before and after, the two sets identical, and
every one resolves on the package installed from the built wheel. The rename is a
relocation and nothing else.

Context

  • Migration for consumers is two lines, both in the CHANGELOG: rewrite each
    import targetprocess as import targetprocess_py (and from targetprocess.<module> likewise), and rename targetprocess to
    targetprocess_py in any logging configuration that names this library's logger.
  • git log --follow needs -M10% on two files. 54 of the 56 src/ files are
    recorded as renames. models.py and resources/__init__.py are recorded as
    delete+add, because both are almost entirely import lines, so every line changed
    and similarity falls below git's default 50% threshold. git log --follow returns
    1 commit for each; git log --follow -M10% returns 2 and 3 respectively. Git
    computes similarity at read time, so this is not fixable in the commit — it just
    needs the flag.
  • CODEOWNERS:12 is a commented-out example of a per-path rule, not a live
    rule. It was updated anyway: one token, and leaving it would cite a directory
    that no longer exists.
  • The development-status classifier stays 4 - Beta — 0.3.0 is still a beta
    release and the public surface may still change between 0.x minors.
  • Known gap, not widened here: the logger namespace is documented only in
    SPEC.md (maintainer-facing) and the CHANGELOG. README.md and docs/USAGE.md
    have no logging section, so a consumer who skims past the CHANGELOG line has no
    second chance to discover the silent-failure change. This is pre-existing — there
    was nothing to rename — and a follow-up rather than a reason to grow this diff.
  • Also noted, not acted on: the targetprocess name is unregistered on PyPI
    (/pypi/targetprocess/json → 404), which is why the bad inference failed rather
    than installing something. That leaves the name available to a third party while
    0.2.x consumers' tooling still asks for it. A registration question, separate from
    the settled no-shim decision.
  • Consumers of this library each need their own follow-up in their own repository
    to move to targetprocess_py and bump their pin; those are tracked separately.

Checklist

  • uv run ruff check . and uv run ruff format --check . pass
  • uv run mypy --strict src passes
  • uv run pytest -q passes and coverage stays at or above 90%
  • uv run pre-commit run --all-files --hook-stage pre-commit passes
  • uv run pre-commit run --all-files --hook-stage pre-push passes
  • [~] New public functions, methods, and classes have docstrings — no new public
    API; every moved docstring is preserved
  • No real credentials, tokens, or PII are added to the diff
  • [~] TODO/FIXME/HACK/XXX markers name an issue — none added

🤖 Generated with Claude Code

man8-octoflow Bot and others added 3 commits September 20, 2026 18:25
BREAKING CHANGE: the import package is now targetprocess_py. Replace every
`import targetprocess` / `from targetprocess…` with `targetprocess_py`. The
distribution stays `targetprocess-py` on PyPI, and the repository keeps its name.

The import name did not match the distribution name, and a tool that infers a
dependency from a top-level import therefore asked PyPI for a package called
`targetprocess`, which does not exist. Windmill is the case in hand: a script
importing this library had to carry inline metadata purely to override that bad
inference, and the same trap waits for every consumer that resolves imports this
way. `targetprocess_py` is the PEP 503 normalisation of `targetprocess-py`, so
the inference now resolves the real distribution with no override.

`src/targetprocess/` moves to `src/targetprocess_py/` and every module reference
follows it: the imports, the `:mod:`/`:class:`/`:func:` docstring references, the
`__module__` prefix test that discovers the entity tree, the coverage target, the
built-wheel smoke import, and the package paths in pyproject.toml, .coderabbit.yaml
and CODEOWNERS' commented example.

Four spellings of the token are deliberately unchanged, because they do not name
the import package:

- the distribution name `targetprocess-py`, everywhere it appears;
- the credential directory `~/.config/targetprocess/.env`, a canonical on-disk
  path shared with tooling outside this repository, whose rename would break
  credential resolution for every consumer of it while passing every test here;
- the vendor's own domains and documentation URLs;
- prose and identifiers naming the product rather than this package: the PyPI
  search keyword, the correlation-ID ContextVar label, and two test names that
  read against TargetProcess's own behaviour and error family.

No compatibility shim. A `targetprocess` module re-exporting the new package
would keep alive exactly the import that cannot be resolved, which is the defect
this change removes.

The recorded cassettes carry no occurrence of the token, so none needed changing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bump `__version__` to 0.3.0 and cut the CHANGELOG section for it. The 0.3.0
heading absorbs the entries that were sitting under `[Unreleased]` — the
ignored-filter guard's four further collections and the eight dropped read-model
range bounds — and adds the import-package rename above them with its migration
line.

A minor bump retires the previous minor, so `SECURITY.md`'s supported-versions
table moves with it, as CONTRIBUTING's release procedure requires.

The development-status classifier stays `4 - Beta`: 0.3.0 is still a beta
release and the public surface may still change between 0.x minors.

The logger-namespace rename lands in the commit after this one, together with its
own CHANGELOG entry, so that reverting it takes the entry with it. This commit is
therefore deliberately not the branch tip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BREAKING CHANGE: the library's logger namespace is now `targetprocess_py`.
Logging configuration that names `targetprocess` — a handler, a level, a filter,
a `dictConfig` entry — stops matching and goes silent; rename it to
`targetprocess_py`.

Python's convention is that a library's root logger carries its top-level package
name, which is what `logging.getLogger(__name__)` yields. After the package
rename that name is `targetprocess_py`, so `getLogger("targetprocess")` named
something no module in this distribution produces: a consumer configuring the
logger they would derive from their own import got nothing, while the library
logged to a namespace they never referenced.

The failure mode differs from the import rename, which is why it is called out
separately and carries its own CHANGELOG entry: an import that no longer resolves
raises at startup, while logging configuration that no longer matches simply stops
producing output, with nothing raised and nothing logged to say so.

`logging.getLogger("targetprocess")` and the child construction in `get_logger()`
move, together with the docstrings that tell a consumer which logger to attach a
handler to, the normative description in SPEC.md, and the record names the
observability tests assert on.

This commit is deliberately last, and carries the CHANGELOG entry for its own
change rather than leaving it in the release commit: the namespace rename is a
judgement separable from the package rename, and a single `git revert` of this
commit must take its documentation with it. A revert that left the entry standing
would tell a consumer to rename logging configuration that had not moved, which
is the silent-logging failure the entry itself warns about.

The correlation-ID ContextVar's label is left as it is. It is module-private and
absent from `__all__`, ContextVar lookup is by object identity rather than by
name, and the emitted log field is `request_id`, so the string surfaces only in a
`repr` or a debugger and names nothing a consumer can configure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@man8-octoflow
man8-octoflow Bot requested a review from man8 as a code owner September 20, 2026 16:52
@coderabbitai

coderabbitai Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 171 files, which is 71 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Repository: man8/targetprocess-py/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 71b5f0fa-9736-456b-9152-6fb79cc93597

📥 Commits

Reviewing files that changed from the base of the PR and between d21da58 and ca256e5.

📒 Files selected for processing (171)
  • .coderabbit.yaml
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • CHANGELOG.md
  • CODEOWNERS
  • CONTRIBUTING.md
  • README.md
  • SECURITY.md
  • SPEC.md
  • docs/USAGE.md
  • examples/bulk_updates.py
  • examples/create_bug.py
  • examples/list_user_stories.py
  • examples/readonly_reporting.py
  • pyproject.toml
  • scripts/check_model_coverage.py
  • scripts/live_smoke.py
  • src/targetprocess/models.py
  • src/targetprocess/resources/__init__.py
  • src/targetprocess_py/__init__.py
  • src/targetprocess_py/_assignables.py
  • src/targetprocess_py/_base.py
  • src/targetprocess_py/_content.py
  • src/targetprocess_py/_dates.py
  • src/targetprocess_py/_entity_types.py
  • src/targetprocess_py/_generals.py
  • src/targetprocess_py/_joins.py
  • src/targetprocess_py/_lookups.py
  • src/targetprocess_py/_nested.py
  • src/targetprocess_py/_observability.py
  • src/targetprocess_py/client.py
  • src/targetprocess_py/exceptions.py
  • src/targetprocess_py/models.py
  • src/targetprocess_py/py.typed
  • src/targetprocess_py/request_handler.py
  • src/targetprocess_py/resources/__init__.py
  • src/targetprocess_py/resources/_verify.py
  • src/targetprocess_py/resources/assignables.py
  • src/targetprocess_py/resources/assignments.py
  • src/targetprocess_py/resources/attachments.py
  • src/targetprocess_py/resources/base.py
  • src/targetprocess_py/resources/bugs.py
  • src/targetprocess_py/resources/comments.py
  • src/targetprocess_py/resources/custom_activities.py
  • src/targetprocess_py/resources/custom_fields.py
  • src/targetprocess_py/resources/custom_rules.py
  • src/targetprocess_py/resources/entities.py
  • src/targetprocess_py/resources/entity_states.py
  • src/targetprocess_py/resources/entity_types.py
  • src/targetprocess_py/resources/epics.py
  • src/targetprocess_py/resources/features.py
  • src/targetprocess_py/resources/iterations.py
  • src/targetprocess_py/resources/priorities.py
  • src/targetprocess_py/resources/processes.py
  • src/targetprocess_py/resources/projects.py
  • src/targetprocess_py/resources/relation_types.py
  • src/targetprocess_py/resources/relations.py
  • src/targetprocess_py/resources/releases.py
  • src/targetprocess_py/resources/requests.py
  • src/targetprocess_py/resources/role_efforts.py
  • src/targetprocess_py/resources/roles.py
  • src/targetprocess_py/resources/severities.py
  • src/targetprocess_py/resources/tasks.py
  • src/targetprocess_py/resources/team_assignments.py
  • src/targetprocess_py/resources/team_iterations.py
  • src/targetprocess_py/resources/teams.py
  • src/targetprocess_py/resources/terms.py
  • src/targetprocess_py/resources/test_cases.py
  • src/targetprocess_py/resources/times.py
  • src/targetprocess_py/resources/user_stories.py
  • src/targetprocess_py/resources/users.py
  • src/targetprocess_py/resources/workflows.py
  • src/targetprocess_py/response_parser.py
  • src/targetprocess_py/transport.py
  • src/targetprocess_py/types.py
  • tests/_support/request_handler.py
  • tests/conftest.py
  • tests/integration/conftest.py
  • tests/integration/test_live_readonly.py
  • tests/integration/test_live_readwrite.py
  • tests/integration/test_live_readwrite_surfaces.py
  • tests/test_assignment.py
  • tests/test_attachment.py
  • tests/test_bug.py
  • tests/test_cassette_guard.py
  • tests/test_client.py
  • tests/test_comment.py
  • tests/test_custom_activity.py
  • tests/test_custom_field.py
  • tests/test_custom_field_value.py
  • tests/test_custom_rule.py
  • tests/test_entity_extras.py
  • tests/test_entity_ref.py
  • tests/test_entity_state.py
  • tests/test_entity_type.py
  • tests/test_epic.py
  • tests/test_exceptions.py
  • tests/test_feature.py
  • tests/test_integration_request_response.py
  • tests/test_iteration.py
  • tests/test_model_field_coverage.py
  • tests/test_models.py
  • tests/test_observability.py
  • tests/test_priority.py
  • tests/test_process.py
  • tests/test_project.py
  • tests/test_query_parameters.py
  • tests/test_relation.py
  • tests/test_relation_type.py
  • tests/test_release.py
  • tests/test_request.py
  • tests/test_request_handler.py
  • tests/test_request_handler_bulk.py
  • tests/test_request_handler_logged_user.py
  • tests/test_resources/test_assignable_state.py
  • tests/test_resources/test_assignments.py
  • tests/test_resources/test_attachments.py
  • tests/test_resources/test_base_resource.py
  • tests/test_resources/test_bugs.py
  • tests/test_resources/test_comments.py
  • tests/test_resources/test_custom_activities.py
  • tests/test_resources/test_custom_field_writes.py
  • tests/test_resources/test_custom_fields.py
  • tests/test_resources/test_custom_rules.py
  • tests/test_resources/test_entities.py
  • tests/test_resources/test_entity_states.py
  • tests/test_resources/test_entity_types.py
  • tests/test_resources/test_epics.py
  • tests/test_resources/test_features.py
  • tests/test_resources/test_ignored_filter_paths.py
  • tests/test_resources/test_iterations.py
  • tests/test_resources/test_lookup_resolvers.py
  • tests/test_resources/test_priorities.py
  • tests/test_resources/test_processes.py
  • tests/test_resources/test_projects.py
  • tests/test_resources/test_relation_types.py
  • tests/test_resources/test_relations.py
  • tests/test_resources/test_releases.py
  • tests/test_resources/test_requests.py
  • tests/test_resources/test_role_efforts.py
  • tests/test_resources/test_roles.py
  • tests/test_resources/test_server_capability.py
  • tests/test_resources/test_severities.py
  • tests/test_resources/test_tasks.py
  • tests/test_resources/test_team_assignments.py
  • tests/test_resources/test_team_iterations.py
  • tests/test_resources/test_teams.py
  • tests/test_resources/test_terms.py
  • tests/test_resources/test_test_cases.py
  • tests/test_resources/test_times.py
  • tests/test_resources/test_user_stories.py
  • tests/test_resources/test_users.py
  • tests/test_resources/test_verified_writes.py
  • tests/test_resources/test_workflows.py
  • tests/test_response_parser.py
  • tests/test_role.py
  • tests/test_role_effort.py
  • tests/test_severity.py
  • tests/test_task.py
  • tests/test_team.py
  • tests/test_team_assignment.py
  • tests/test_team_iteration.py
  • tests/test_term.py
  • tests/test_test_case.py
  • tests/test_time.py
  • tests/test_transport.py
  • tests/test_types.py
  • tests/test_uploaded_attachment.py
  • tests/test_user.py
  • tests/test_user_story.py
  • tests/test_workflow.py

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@man8

man8 commented Sep 20, 2026

Copy link
Copy Markdown
Owner

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review skipped: 171 files exceed the limit of 100.

@man8
man8 merged commit d93c8d1 into main Sep 20, 2026
8 checks passed
@man8
man8 deleted the rename-import-package branch September 20, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant