You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by sb...@apache.org on 2026/06/08 20:18:35 UTC

(tooling-trusted-releases) branch tertia updated (80185be3 -> 81b1e00e)

This is an automated email from the ASF dual-hosted git repository.

sbp pushed a change to branch tertia
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git


 discard 80185be3 Avoid shadowing a standard library module, and add a lint
     new 81b1e00e Avoid shadowing a standard library module, and add a lint

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (80185be3)
            \
             N -- N -- N   refs/heads/tertia (81b1e00e)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 tests/unit/test_keys_writer.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org


(tooling-trusted-releases) 01/01: Avoid shadowing a standard library module, and add a lint

Posted by sb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch tertia
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git

commit 81b1e00ed99f0751984e22ccb7bc8a84be7d4d21
Author: Sean B. Palmer <se...@miscoranda.com>
AuthorDate: Mon Jun 8 21:03:24 2026 +0100

    Avoid shadowing a standard library module, and add a lint
---
 atr/api/__init__.py                           | 16 +++---
 atr/get/checks.py                             |  8 +--
 atr/get/tokens.py                             |  4 +-
 atr/post/draft.py                             |  6 +--
 atr/post/keys.py                              | 12 ++---
 atr/render.py                                 | 10 ++--
 atr/shared/keys.py                            |  4 +-
 atr/ssh.py                                    | 10 ++--
 atr/storage/{types.py => datatypes.py}        |  0
 atr/storage/readers/checks.py                 |  6 +--
 atr/storage/readers/releases.py               | 24 ++++-----
 atr/storage/readers/tokens.py                 | 10 ++--
 atr/storage/writers/keys.py                   | 72 +++++++++++++--------------
 atr/storage/writers/release.py                | 58 ++++++++++-----------
 atr/storage/writers/revision.py               | 14 +++---
 atr/storage/writers/tokens.py                 | 14 +++---
 atr/util.py                                   |  4 +-
 pyproject.toml                                |  4 ++
 scripts/keys_import.py                        |  6 +--
 tests/unit/test_checker_stats.py              |  8 +--
 tests/unit/test_checks_table.py               |  8 +--
 tests/unit/test_concern_acknowledgements.py   | 10 ++--
 tests/unit/test_create_revision.py            | 14 +++---
 tests/unit/test_create_revision_quarantine.py |  4 +-
 tests/unit/test_keys_writer.py                | 14 +++---
 tests/unit/test_release_writer_hashgen.py     | 10 ++--
 tests/unit/test_source_artifact_check.py      | 22 ++++----
 tests/unit/test_svn_publish_writer.py         |  4 +-
 tests/unit/test_tokens_safe.py                | 12 ++---
 29 files changed, 197 insertions(+), 191 deletions(-)

diff --git a/atr/api/__init__.py b/atr/api/__init__.py
index 48414a72..4561099d 100644
--- a/atr/api/__init__.py
+++ b/atr/api/__init__.py
@@ -47,8 +47,8 @@ import atr.paths as paths
 import atr.principal as principal
 import atr.shared.catalog as catalog
 import atr.storage as storage
+import atr.storage.datatypes as datatypes
 import atr.storage.outcome as outcome
-import atr.storage.types as types
 import atr.tabulate as tabulate
 import atr.user as user
 import atr.util as util
@@ -688,15 +688,15 @@ async def key_add(
 
     async with storage.write(asf_uid) as write:
         wafc = write.as_foundation_committer()
-        ocr: outcome.Outcome[types.Key] = await wafc.keys.ensure_stored_one(data.key)
+        ocr: outcome.Outcome[datatypes.Key] = await wafc.keys.ensure_stored_one(data.key)
         try:
             key = ocr.result_or_raise()
-        except types.UnknownApacheUidError as e:
+        except datatypes.UnknownApacheUidError as e:
             raise exceptions.BadRequest(str(e)) from e
 
         for selected_committee_key in selected_committee_keys:
             wacm = write.as_committee_member(selected_committee_key)
-            oc: outcome.Outcome[types.LinkedCommittee] = await wacm.keys.associate_fingerprint(
+            oc: outcome.Outcome[datatypes.LinkedCommittee] = await wacm.keys.associate_fingerprint(
                 key.key_model.fingerprint
             )
             oc.result_or_raise()
@@ -797,11 +797,11 @@ async def keys_upload(
     selected_committee_key = data.committee
     async with storage.write(asf_uid) as write:
         wacm = write.as_committee_member(selected_committee_key)
-        outcomes: outcome.List[types.Key] = await wacm.keys.ensure_associated(filetext)
+        outcomes: outcome.List[datatypes.Key] = await wacm.keys.ensure_associated(filetext)
 
         # TODO: It would be nice to serialise the actual outcomes
         # Or, perhaps better yet, to have a standard datatype mapping
-        # This would be specified in models.api, then imported into storage.types
+        # This would be specified in models.api, then imported into storage.datatypes
         # Or perhaps it should go in models.storage or models.outcomes
         api_outcomes = []
         for oc in outcomes.outcomes():
@@ -815,7 +815,7 @@ async def keys_upload(
                 case outcome.Error(error):
                     # TODO: This branch means we must improve the return type
                     match error:
-                        case types.PublicKeyError() as pke:
+                        case datatypes.PublicKeyError() as pke:
                             api_outcome = models.api.KeysUploadException(
                                 status="error",
                                 key=pke.key.key_model,
@@ -1422,7 +1422,7 @@ async def release_upload(
         async with storage.write(asf_uid) as write:
             wacp = await write.as_project_committee_participant(data.project)
             result = await wacp.release.upload_file(data)
-    except types.PhaseMismatchError as e:
+    except datatypes.PhaseMismatchError as e:
         raise exceptions.Conflict(str(e))
     if isinstance(result, sql.Quarantined):
         return {
diff --git a/atr/get/checks.py b/atr/get/checks.py
index efd6f1a7..47a551a4 100644
--- a/atr/get/checks.py
+++ b/atr/get/checks.py
@@ -40,7 +40,7 @@ import atr.post as post
 import atr.render as render
 import atr.shared.draft as draft
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.template as template
 import atr.util as util
 import atr.web as web
@@ -202,7 +202,7 @@ async def selected_revision(
 
 
 def _classification_badge_cell(
-    info: types.PathInfo | None, path: safe.RelPath, severity: sql.CheckResultStatus | None
+    info: datatypes.PathInfo | None, path: safe.RelPath, severity: sql.CheckResultStatus | None
 ) -> htm.Element:
     file_type = info.file_types.get(path) if (info is not None) else None
     match file_type:
@@ -313,7 +313,7 @@ def _render_checks_table(
     release: sql.Release,
     paths: list[safe.RelPath],
     per_file_stats: dict[safe.RelPath, FileStats],
-    info: types.PathInfo | None,
+    info: datatypes.PathInfo | None,
 ) -> None:
     if not paths:
         page.div(".alert.alert-info")["This release candidate does not have any files."]
@@ -346,7 +346,7 @@ def _render_file_row(
     release: sql.Release,
     path: safe.RelPath,
     stats: FileStats,
-    info: types.PathInfo | None,
+    info: datatypes.PathInfo | None,
 ) -> None:
     path_str = str(path)
     num_style = "font-size: 1.1rem;"
diff --git a/atr/get/tokens.py b/atr/get/tokens.py
index 441d6a84..8b01fd30 100644
--- a/atr/get/tokens.py
+++ b/atr/get/tokens.py
@@ -23,7 +23,7 @@ import atr.htm as htm
 import atr.post as post
 import atr.shared as shared
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.template as template
 import atr.util as util
 import atr.web as web
@@ -103,7 +103,7 @@ async def tokens(_session: web.Committer, _tokens: Literal["tokens"]) -> str:
     )
 
 
-async def _build_tokens_table(page: htm.Block, tokens_list: list[types.PersonalAccessTokenSafe]) -> None:
+async def _build_tokens_table(page: htm.Block, tokens_list: list[datatypes.PersonalAccessTokenSafe]) -> None:
     if not tokens_list:
         page.p["No tokens found."]
         return
diff --git a/atr/post/draft.py b/atr/post/draft.py
index b90fb45b..57607d17 100644
--- a/atr/post/draft.py
+++ b/atr/post/draft.py
@@ -32,7 +32,7 @@ import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.shared as shared
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.util as util
 import atr.web as web
 
@@ -64,7 +64,7 @@ async def cache_reset(
                 description=description,
                 reset_to_global_cache=True,
             )
-    except types.PhaseMismatchError as e:
+    except datatypes.PhaseMismatchError as e:
         return await session.redirect(
             get.compose.selected,
             project_key=str(project_key),
@@ -227,7 +227,7 @@ async def recheck(
                 description=description,
                 set_local_cache=True,
             )
-    except types.PhaseMismatchError as e:
+    except datatypes.PhaseMismatchError as e:
         return await session.redirect(
             get.compose.selected,
             project_key=str(project_key),
diff --git a/atr/post/keys.py b/atr/post/keys.py
index 58f2b30d..c038450b 100644
--- a/atr/post/keys.py
+++ b/atr/post/keys.py
@@ -39,8 +39,8 @@ import atr.paths as paths
 import atr.sessions as sessions
 import atr.shared as shared
 import atr.storage as storage
+import atr.storage.datatypes as datatypes
 import atr.storage.outcome as outcome
-import atr.storage.types as types
 import atr.util as util
 import atr.web as web
 
@@ -70,18 +70,18 @@ async def add(
 
         async with storage.write() as write:
             wafc = write.as_foundation_committer()
-            ocr: outcome.Outcome[types.Key] = await wafc.keys.ensure_stored_one(key_text)
+            ocr: outcome.Outcome[datatypes.Key] = await wafc.keys.ensure_stored_one(key_text)
             key = ocr.result_or_raise()
 
             for selected_committee_key in selected_committee_keys:
                 wacp = write.as_committee_participant(selected_committee_key)
-                oc: outcome.Outcome[types.LinkedCommittee] = await wacp.keys.associate_fingerprint(
+                oc: outcome.Outcome[datatypes.LinkedCommittee] = await wacp.keys.associate_fingerprint(
                     key.key_model.fingerprint
                 )
                 oc.result_or_raise()
 
             fingerprint_upper = key.key_model.fingerprint.upper()
-            if key.status == types.KeyStatus.PARSED:
+            if key.status == datatypes.KeyStatus.PARSED:
                 details_url = util.as_url(get.keys.details, fingerprint=key.key_model.fingerprint)
                 p = htm.p[
                     f"OpenPGP key {fingerprint_upper} was already in the database. ",
@@ -95,7 +95,7 @@ async def add(
 
     except PrivateKeyUploadError:
         await quart.flash(util.PRIVATE_KEY_UPLOAD_WARNING, "error")
-    except types.UnknownApacheUidError as e:
+    except datatypes.UnknownApacheUidError as e:
         await quart.flash(str(e), "error")
     except web.FlashError as e:
         log.warning(f"FlashError adding OpenPGP key: {e}")
@@ -152,7 +152,7 @@ async def import_selected_revision(
     await session.release(project_key, version_key, with_committee=False, with_project=False)
     async with storage.write() as write:
         wacm = await write.as_project_committee_member(project_key)
-        outcomes: outcome.List[types.Key] = await wacm.keys.import_keys_file(project_key, version_key)
+        outcomes: outcome.List[datatypes.Key] = await wacm.keys.import_keys_file(project_key, version_key)
 
     message = f"Uploaded {util.plural(outcomes.result_count, 'key')}"
     if outcomes.error_count > 0:
diff --git a/atr/render.py b/atr/render.py
index b867925e..4759fdb5 100644
--- a/atr/render.py
+++ b/atr/render.py
@@ -26,7 +26,7 @@ import atr.get as get
 import atr.htm as htm
 import atr.models.safe as safe
 import atr.models.sql as sql
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.util as util
 
 type Phase = Literal["COMPOSE", "VOTE", "FINISH"]
@@ -309,7 +309,7 @@ def html_recipients_to_radios(
 
 
 def render_checks_summary(
-    info: types.PathInfo | None, project_key: safe.ProjectKey, version_key: safe.VersionKey
+    info: datatypes.PathInfo | None, project_key: safe.ProjectKey, version_key: safe.VersionKey
 ) -> htm.Element | None:
     if info is None:
         return None
@@ -346,7 +346,7 @@ def render_checks_summary(
     return card.collect()
 
 
-def render_exception_banner(info: types.PathInfo) -> htm.Element | None:
+def render_exception_banner(info: datatypes.PathInfo) -> htm.Element | None:
     path_count = sum(len(results) for results in info.exceptions.values())
     release_count = len(info.release_level_exceptions)
     total = path_count + release_count
@@ -375,7 +375,7 @@ def render_exception_banner(info: types.PathInfo) -> htm.Element | None:
 
 
 def _render_checker_entry(
-    stat: types.CheckerStats | None,
+    stat: datatypes.CheckerStats | None,
     release_problems: list[sql.CheckResult],
     index: int,
     project_key: safe.ProjectKey,
@@ -419,7 +419,7 @@ def _render_release_problem(result: sql.CheckResult) -> list[htm.Element | str]:
 
 def _render_stat_files(
     files_div: htm.Block,
-    stat: types.CheckerStats,
+    stat: datatypes.CheckerStats,
     project_key: safe.ProjectKey,
     version_key: safe.VersionKey,
 ) -> None:
diff --git a/atr/shared/keys.py b/atr/shared/keys.py
index eff61859..f408c12a 100644
--- a/atr/shared/keys.py
+++ b/atr/shared/keys.py
@@ -26,7 +26,7 @@ import atr.form as form
 import atr.htm as htm
 import atr.shared as shared
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.template as template
 import atr.util as util
 
@@ -293,7 +293,7 @@ def _render_results_table(
             fingerprint = key_obj.key_model.fingerprint if key_obj else "UNKNOWN"
             email_addr = key_obj.key_model.primary_declared_uid if key_obj else ""
             # Check whether the LINKED flag is set
-            added_flag = bool(key_obj.status & types.KeyStatus.LINKED) if key_obj else False
+            added_flag = bool(key_obj.status & datatypes.KeyStatus.LINKED) if key_obj else False
             error_flag = False
         else:
             err = outcome.error_or_none()
diff --git a/atr/ssh.py b/atr/ssh.py
index 76cee3be..a11afb9f 100644
--- a/atr/ssh.py
+++ b/atr/ssh.py
@@ -46,7 +46,7 @@ import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.paths as paths
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.user as user
 import atr.util as util
 
@@ -459,7 +459,7 @@ async def _step_04_command_validate(
 
 
 async def _step_05a_command_path_validate_read(path: str) -> tuple[safe.ProjectKey, safe.VersionKey, str | None]:
-    """Validate the path argument for rsync read commands, returning safe types."""
+    """Validate the path argument for rsync read commands, returning safe datatypes."""
     # READ: rsync --server --sender -vlogDtpre.iLsfxCIvu . /proj/v1/
     # Validating path: /proj/v1/
     _validate_path_common(path)
@@ -490,7 +490,7 @@ async def _step_05a_command_path_validate_read(path: str) -> tuple[safe.ProjectK
 
 
 async def _step_05b_command_path_validate_write(path: str) -> tuple[safe.ProjectKey, safe.VersionKey, None]:
-    """Validate the path argument for rsync write commands, returning safe types."""
+    """Validate the path argument for rsync write commands, returning safe datatypes."""
     # WRITE: rsync --server -vlogDtpre.iLsfxCIvu . /proj/v1/
     # Validating path: /proj/v1/
     _validate_path_common(path)
@@ -667,7 +667,7 @@ async def _step_07b_process_validated_rsync_write(
                     f"rsync upload failed with exit status {exit_status} for {for_revision}. "
                     f"Command: {process.command} (run as {' '.join(rsync_argv)})"
                 )
-                raise types.FailedError(f"rsync upload failed with exit status {exit_status} for {for_revision}")
+                raise datatypes.FailedError(f"rsync upload failed with exit status {exit_status} for {for_revision}")
 
         try:
             result = await wacp.revision.create_revision_with_quarantine(
@@ -694,7 +694,7 @@ async def _step_07b_process_validated_rsync_write(
             if not process.stderr.is_closing():
                 process.stderr.write(message.encode())
                 await process.stderr.drain()
-        except types.FailedError:
+        except datatypes.FailedError:
             log.info(f"rsync upload unsuccessful for release {release_key}")
 
         # If we got here, there was no exception
diff --git a/atr/storage/types.py b/atr/storage/datatypes.py
similarity index 100%
rename from atr/storage/types.py
rename to atr/storage/datatypes.py
diff --git a/atr/storage/readers/checks.py b/atr/storage/readers/checks.py
index 53096479..0811fc5c 100644
--- a/atr/storage/readers/checks.py
+++ b/atr/storage/readers/checks.py
@@ -25,7 +25,7 @@ import atr.db.interaction as interaction
 import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.util as util
 
 if TYPE_CHECKING:
@@ -46,7 +46,7 @@ class GeneralPublic:
         self.__data = data
         self.__asf_uid = read.authorisation.asf_uid
 
-    async def by_release_path(self, release: sql.Release, rel_path: pathlib.Path) -> types.CheckResults:
+    async def by_release_path(self, release: sql.Release, rel_path: pathlib.Path) -> datatypes.CheckResults:
         if release.latest_revision_number is None:
             raise ValueError("Release has no revision - Invalid state")
 
@@ -77,7 +77,7 @@ class GeneralPublic:
         # Order member results by relative path and then by checker name
         for member_rel_path in sorted(member_results_list.keys()):
             member_results_list[member_rel_path].sort(key=lambda r: r.checker)
-        return types.CheckResults(primary_results_list, member_results_list, ignored_checks)
+        return datatypes.CheckResults(primary_results_list, member_results_list, ignored_checks)
 
     async def ignores(self, project_key: safe.ProjectKey) -> list[sql.CheckResultIgnore]:
         results = await self.__data.check_result_ignore(
diff --git a/atr/storage/readers/releases.py b/atr/storage/readers/releases.py
index 16cbc097..30a796fa 100644
--- a/atr/storage/readers/releases.py
+++ b/atr/storage/readers/releases.py
@@ -28,7 +28,7 @@ import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.paths as paths
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 
 
 @dataclasses.dataclass
@@ -50,8 +50,8 @@ class GeneralPublic:
         self.__data = data
         self.__asf_uid = read.authorisation.asf_uid
 
-    async def path_info(self, release: sql.Release, all_paths: list[safe.RelPath]) -> types.PathInfo | None:
-        info = types.PathInfo()
+    async def path_info(self, release: sql.Release, all_paths: list[safe.RelPath]) -> datatypes.PathInfo | None:
+        info = datatypes.PathInfo()
         latest_revision_number = release.latest_revision_number
         if latest_revision_number is None:
             return None
@@ -95,7 +95,7 @@ class GeneralPublic:
                     files_for_status = acc.files.setdefault(status, {})
                     files_for_status[path_str] = files_for_status.get(path_str, 0) + 1
 
-    def __compute_checker_stats(self, info: types.PathInfo, paths: list[safe.RelPath]) -> None:
+    def __compute_checker_stats(self, info: datatypes.PathInfo, paths: list[safe.RelPath]) -> None:
         paths_set = set(paths)
         checker_data: dict[str, CheckerAccumulator] = {}
 
@@ -112,7 +112,7 @@ class GeneralPublic:
             if non_note_total == 0:
                 continue
             info.checker_stats.append(
-                types.CheckerStats(
+                datatypes.CheckerStats(
                     checker=checker,
                     counts=acc.counts,
                     files=acc.files,
@@ -120,14 +120,14 @@ class GeneralPublic:
             )
 
     async def __notes_suggestions_concerns(
-        self, release: sql.Release, latest_revision_number: safe.RevisionNumber, info: types.PathInfo
+        self, release: sql.Release, latest_revision_number: safe.RevisionNumber, info: datatypes.PathInfo
     ) -> None:
         match_ignore = await self.__read_as.checks.ignores_matcher(release.safe_project_key)
         attestable_checks = await interaction.checks_for(
             release, revision=latest_revision_number, caller_data=self.__data
         )
 
-        cs = types.ChecksSubset(
+        cs = datatypes.ChecksSubset(
             checks=attestable_checks,
             info=info,
             match_ignore=match_ignore,
@@ -140,7 +140,7 @@ class GeneralPublic:
         await self.__exceptions(cs)
         await self.__blocker(cs)
 
-    async def __blocker(self, cs: types.ChecksSubset) -> None:
+    async def __blocker(self, cs: datatypes.ChecksSubset) -> None:
         blocker = [cr for cr in cs.checks if cr.status == sql.CheckResultStatus.BLOCKER]
         for result in blocker:
             if path := result.safe_primary_rel_path:
@@ -148,7 +148,7 @@ class GeneralPublic:
             else:
                 cs.info.release_level_blockers.append(result)
 
-    async def __concerns(self, cs: types.ChecksSubset) -> None:
+    async def __concerns(self, cs: datatypes.ChecksSubset) -> None:
         concerns = [cr for cr in cs.checks if cr.status == sql.CheckResultStatus.CONCERN]
         for concern in concerns:
             if cs.match_ignore(concern):
@@ -159,7 +159,7 @@ class GeneralPublic:
             else:
                 cs.info.release_level_concerns.append(concern)
 
-    async def __exceptions(self, cs: types.ChecksSubset) -> None:
+    async def __exceptions(self, cs: datatypes.ChecksSubset) -> None:
         exceptions = [cr for cr in cs.checks if cr.status == sql.CheckResultStatus.EXCEPTION]
         for exception in exceptions:
             if cs.match_ignore(exception):
@@ -170,14 +170,14 @@ class GeneralPublic:
             else:
                 cs.info.release_level_exceptions.append(exception)
 
-    async def __notes(self, cs: types.ChecksSubset) -> None:
+    async def __notes(self, cs: datatypes.ChecksSubset) -> None:
         notes = [cr for cr in cs.checks if cr.status == sql.CheckResultStatus.NOTE]
         for note in notes:
             # Notes cannot be ignored
             if path := note.safe_primary_rel_path:
                 cs.info.notes.setdefault(path, []).append(note)
 
-    async def __suggestions(self, cs: types.ChecksSubset) -> None:
+    async def __suggestions(self, cs: datatypes.ChecksSubset) -> None:
         suggestions = [cr for cr in cs.checks if cr.status == sql.CheckResultStatus.SUGGESTION]
         for suggestion in suggestions:
             if cs.match_ignore(suggestion):
diff --git a/atr/storage/readers/tokens.py b/atr/storage/readers/tokens.py
index 0119cb10..087f0cb8 100644
--- a/atr/storage/readers/tokens.py
+++ b/atr/storage/readers/tokens.py
@@ -23,7 +23,7 @@ import sqlmodel
 import atr.db as db
 import atr.models.sql as sql
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 
 
 class GeneralPublic:
@@ -41,7 +41,7 @@ class FoundationCommitter(GeneralPublic):
         self.__read_as = read_as
         self.__data = data
 
-    async def own_personal_access_tokens(self) -> list[types.PersonalAccessTokenSafe]:
+    async def own_personal_access_tokens(self) -> list[datatypes.PersonalAccessTokenSafe]:
         asf_uid = self.__read.authorisation.asf_uid
         if asf_uid is None:
             raise ValueError("Not authorized")
@@ -49,9 +49,9 @@ class FoundationCommitter(GeneralPublic):
         tokens = await (
             self.__data.personal_access_token(asfuid=asf_uid).order_by(via(sql.PersonalAccessToken.created)).all()
         )
-        return [types.PersonalAccessTokenSafe.from_sql(token) for token in tokens]
+        return [datatypes.PersonalAccessTokenSafe.from_sql(token) for token in tokens]
 
-    async def most_recent_jwt_pat(self) -> types.PersonalAccessTokenSafe | None:
+    async def most_recent_jwt_pat(self) -> datatypes.PersonalAccessTokenSafe | None:
         # , asf_uid: str | None = None
         # if asf_uid is None:
         asf_uid = self.__read.authorisation.asf_uid
@@ -69,4 +69,4 @@ class FoundationCommitter(GeneralPublic):
         token = await self.__data.query_one_or_none(stmt)
         if token is None:
             return None
-        return types.PersonalAccessTokenSafe.from_sql(token)
+        return datatypes.PersonalAccessTokenSafe.from_sql(token)
diff --git a/atr/storage/writers/keys.py b/atr/storage/writers/keys.py
index 890b81ac..36d5bd50 100644
--- a/atr/storage/writers/keys.py
+++ b/atr/storage/writers/keys.py
@@ -42,8 +42,8 @@ import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.paths as paths
 import atr.storage as storage
+import atr.storage.datatypes as datatypes
 import atr.storage.outcome as outcome
-import atr.storage.types as types
 import atr.svn as svn
 import atr.tasks as tasks
 import atr.user as user
@@ -234,7 +234,7 @@ class FoundationCommitter(GeneralPublic):
         except Exception as e:
             return outcome.Error(e)
 
-    async def ensure_stored_one(self, key_file_text: str) -> outcome.Outcome[types.Key]:
+    async def ensure_stored_one(self, key_file_text: str) -> outcome.Outcome[datatypes.Key]:
         return await self.__ensure_one(key_file_text, associate=False)
 
     def public_key_model(
@@ -484,7 +484,7 @@ class FoundationCommitter(GeneralPublic):
 
         return affected
 
-    def __block_model(self, key_block: str, ldap_data: cache.EmailUidLookup) -> types.Key:
+    def __block_model(self, key_block: str, ldap_data: cache.EmailUidLookup) -> datatypes.Key:
         # This cache is only held for the session
         if key_block in self.__key_block_models_cache:
             cached_key_models = self.__key_block_models_cache[key_block]
@@ -497,16 +497,16 @@ class FoundationCommitter(GeneralPublic):
         self.__key_block_models_cache[key_block] = [key]
         return key
 
-    def __block_model_create(self, key_block: str, ldap_data: cache.EmailUidLookup) -> types.Key:
+    def __block_model_create(self, key_block: str, ldap_data: cache.EmailUidLookup) -> datatypes.Key:
         public_key, _ = openpgp.PublicKey.from_armor(key_block)
         key_model = self.public_key_model(public_key, ldap_data, original_key_block=key_block)
         _validate_key_strength(key_model.algorithm, key_model.length, key_model.created)
-        return types.Key(status=types.KeyStatus.PARSED, key_model=key_model)
+        return datatypes.Key(status=datatypes.KeyStatus.PARSED, key_model=key_model)
 
     async def __database_add_model(
         self,
-        key: types.Key,
-    ) -> outcome.Outcome[types.Key]:
+        key: datatypes.Key,
+    ) -> outcome.Outcome[datatypes.Key]:
         via = sql.validate_instrumented_attribute
 
         await self.__data.begin_immediate()
@@ -522,7 +522,7 @@ class FoundationCommitter(GeneralPublic):
 
         if key_insert_result.one_or_none() is None:
             log.info(f"Key {key.key_model.fingerprint} already exists in database")
-            return outcome.Result(types.Key(status=types.KeyStatus.PARSED, key_model=key.key_model))
+            return outcome.Result(datatypes.Key(status=datatypes.KeyStatus.PARSED, key_model=key.key_model))
 
         log.info(f"Inserted key {key.key_model.fingerprint}")
         self.__write_as.append_to_audit_log(
@@ -533,9 +533,9 @@ class FoundationCommitter(GeneralPublic):
         )
 
         # TODO: PARSED now acts as "ALREADY_ADDED"
-        return outcome.Result(types.Key(status=types.KeyStatus.INSERTED, key_model=key.key_model))
+        return outcome.Result(datatypes.Key(status=datatypes.KeyStatus.INSERTED, key_model=key.key_model))
 
-    async def __ensure_one(self, key_file_text: str, associate: bool = True) -> outcome.Outcome[types.Key]:
+    async def __ensure_one(self, key_file_text: str, associate: bool = True) -> outcome.Outcome[datatypes.Key]:
         try:
             key_blocks = util.parse_key_blocks(key_file_text)
         except Exception as e:
@@ -554,7 +554,7 @@ class FoundationCommitter(GeneralPublic):
             return outcome.Error(e)
         if key.key_model.apache_uid is None:
             return outcome.Error(
-                types.UnknownApacheUidError(
+                datatypes.UnknownApacheUidError(
                     "OpenPGP key could not be associated with an ASF UID. Import it through a KEYS file instead."
                 )
             )
@@ -658,7 +658,7 @@ class CommitteeParticipant(FoundationCommitter):
         self.__asf_uid = asf_uid
         self.__committee_key = committee_key
 
-    async def associate_fingerprint(self, fingerprint: str) -> outcome.Outcome[types.LinkedCommittee]:
+    async def associate_fingerprint(self, fingerprint: str) -> outcome.Outcome[datatypes.LinkedCommittee]:
         via = sql.validate_instrumented_attribute
         link_values = [{"committee_key": self.__committee_key, "key_fingerprint": fingerprint}]
         try:
@@ -689,7 +689,7 @@ class CommitteeParticipant(FoundationCommitter):
         except Exception as e:
             return outcome.Error(e)
         return outcome.Result(
-            types.LinkedCommittee(
+            datatypes.LinkedCommittee(
                 name=self.__committee_key,
                 autogenerated_keys_file=autogenerated_outcome,
             )
@@ -722,21 +722,21 @@ class CommitteeParticipant(FoundationCommitter):
     def committee_key(self) -> str:
         return self.__committee_key
 
-    async def ensure_associated(self, keys_file_text: str) -> outcome.List[types.Key]:
-        outcomes: outcome.List[types.Key] = await self.__ensure(keys_file_text, associate=True)
+    async def ensure_associated(self, keys_file_text: str) -> outcome.List[datatypes.Key]:
+        outcomes: outcome.List[datatypes.Key] = await self.__ensure(keys_file_text, associate=True)
         if outcomes.any_result:
             await self.autogenerate_keys_file()
         return outcomes
 
-    async def ensure_stored(self, keys_file_text: str) -> outcome.List[types.Key]:
-        outcomes: outcome.List[types.Key] = await self.__ensure(keys_file_text, associate=False)
+    async def ensure_stored(self, keys_file_text: str) -> outcome.List[datatypes.Key]:
+        outcomes: outcome.List[datatypes.Key] = await self.__ensure(keys_file_text, associate=False)
         if outcomes.any_result:
             await self.autogenerate_keys_file()
         return outcomes
 
     async def import_keys_file(
         self, project_key: safe.ProjectKey, version_key: safe.VersionKey
-    ) -> outcome.List[types.Key]:
+    ) -> outcome.List[datatypes.Key]:
         release = await self.__data.release(
             project_key=str(project_key),
             version=str(version_key),
@@ -784,7 +784,7 @@ class CommitteeParticipant(FoundationCommitter):
         )
         return outcomes
 
-    def __block_models(self, key_block: str, ldap_data: cache.EmailUidLookup) -> list[types.Key | Exception]:
+    def __block_models(self, key_block: str, ldap_data: cache.EmailUidLookup) -> list[datatypes.Key | Exception]:
         try:
             public_key, _ = openpgp.PublicKey.from_armor(key_block)
         except Exception as e:
@@ -793,15 +793,15 @@ class CommitteeParticipant(FoundationCommitter):
         try:
             key_model = self.public_key_model(public_key, ldap_data, original_key_block=key_block)
             _validate_key_strength(key_model.algorithm, key_model.length, key_model.created)
-            key = types.Key(status=types.KeyStatus.PARSED, key_model=key_model)
+            key = datatypes.Key(status=datatypes.KeyStatus.PARSED, key_model=key_model)
             key_list.append(key)
         except Exception as e:
             key_list.append(e)
         return key_list
 
     async def __database_add_models(
-        self, outcomes: outcome.List[types.Key], associate: bool = True
-    ) -> outcome.List[types.Key]:
+        self, outcomes: outcome.List[datatypes.Key], associate: bool = True
+    ) -> outcome.List[datatypes.Key]:
         # Try to upsert all models and link to the committee in one transaction
         try:
             outcomes = await self.__database_add_models_core(outcomes, associate=associate)
@@ -809,20 +809,20 @@ class CommitteeParticipant(FoundationCommitter):
             # This logging is just so that ruff does not erase e
             log.info(f"Post-parse error: {e}")
 
-            def raise_post_parse_error(key: types.Key) -> NoReturn:
+            def raise_post_parse_error(key: datatypes.Key) -> NoReturn:
                 nonlocal e
                 # We assume here that the transaction was rolled back correctly
-                key = types.Key(status=types.KeyStatus.PARSED, key_model=key.key_model)
-                raise types.PublicKeyError(key, e)
+                key = datatypes.Key(status=datatypes.KeyStatus.PARSED, key_model=key.key_model)
+                raise datatypes.PublicKeyError(key, e)
 
             outcomes.update_roes(Exception, raise_post_parse_error)
         return outcomes
 
     async def __database_add_models_core(  # noqa: C901
         self,
-        outcomes: outcome.List[types.Key],
+        outcomes: outcome.List[datatypes.Key],
         associate: bool = True,
-    ) -> outcome.List[types.Key]:
+    ) -> outcome.List[datatypes.Key]:
         via = sql.validate_instrumented_attribute
         key_list = outcomes.results()
 
@@ -846,9 +846,9 @@ class CommitteeParticipant(FoundationCommitter):
             key_inserts = set()
             log.info("Inserted 0 keys (no keys to insert)")
 
-        def replace_with_inserted(key: types.Key) -> types.Key:
+        def replace_with_inserted(key: datatypes.Key) -> datatypes.Key:
             if key.key_model.fingerprint in key_inserts:
-                key.status = types.KeyStatus.INSERTED
+                key.status = datatypes.KeyStatus.INSERTED
             return key
 
         outcomes.update_roes(Exception, replace_with_inserted)
@@ -870,15 +870,15 @@ class CommitteeParticipant(FoundationCommitter):
             link_inserts = {row.key_fingerprint for row in link_insert_result}
             log.info(f"Inserted {util.plural(len(link_inserts), 'key link')}")
 
-            def replace_with_linked(key: types.Key) -> types.Key:
+            def replace_with_linked(key: datatypes.Key) -> datatypes.Key:
                 # nonlocal link_inserts
                 match key:
-                    case types.Key(status=types.KeyStatus.INSERTED):
+                    case datatypes.Key(status=datatypes.KeyStatus.INSERTED):
                         if key.key_model.fingerprint in link_inserts:
-                            key.status = types.KeyStatus.INSERTED_AND_LINKED
-                    case types.Key(status=types.KeyStatus.PARSED):
+                            key.status = datatypes.KeyStatus.INSERTED_AND_LINKED
+                    case datatypes.Key(status=datatypes.KeyStatus.PARSED):
                         if key.key_model.fingerprint in link_inserts:
-                            key.status = types.KeyStatus.LINKED
+                            key.status = datatypes.KeyStatus.LINKED
                 return key
 
             outcomes.update_roes(Exception, replace_with_linked)
@@ -902,8 +902,8 @@ class CommitteeParticipant(FoundationCommitter):
         self,
         keys_file_text: str,
         associate: bool = True,
-    ) -> outcome.List[types.Key]:
-        outcomes = outcome.List[types.Key]()
+    ) -> outcome.List[datatypes.Key]:
+        outcomes = outcome.List[datatypes.Key]()
         try:
             ldap_data = await cache.email_uid_view_or_live()
             key_blocks = util.parse_key_blocks(keys_file_text)
diff --git a/atr/storage/writers/release.py b/atr/storage/writers/release.py
index 978dd9e3..c9203ef2 100644
--- a/atr/storage/writers/release.py
+++ b/atr/storage/writers/release.py
@@ -47,7 +47,7 @@ import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.paths as paths
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.svn as svn
 import atr.tasks.checks as checks
 import atr.tasks.checks.signature as signature
@@ -89,7 +89,7 @@ async def _signature_provenance_metadata_for(
     signature_rel_path: pathlib.Path,
 ) -> dict[str, str]:
     if parent_revision is None:
-        raise types.FailedError("SHA512 generation requires a parent revision with a verified signature.")
+        raise datatypes.FailedError("SHA512 generation requires a parent revision with a verified signature.")
     release_key = sql.release_key(str(project_key), str(version_key))
     parent_revision_number = parent_revision.safe_number
     parent_number = str(parent_revision_number)
@@ -107,7 +107,7 @@ async def _signature_provenance_metadata_for(
             "SHA512 generation is waiting for signature verification"
             f" release={release_key} revision={parent_number} path={signature_rel_str}"
         )
-        raise types.FailedError(
+        raise datatypes.FailedError(
             "Signature verification has not completed yet for"
             f" {signature_rel_path.name}. Please wait for checks to finish and try again."
         )
@@ -121,8 +121,10 @@ async def _signature_provenance_metadata_for(
     )
     if latest.status != sql.CheckResultStatus.NOTE:
         if latest_message:
-            raise types.FailedError(f"Signature verification for {signature_rel_path.name} failed: {latest_message}")
-        raise types.FailedError(
+            raise datatypes.FailedError(
+                f"Signature verification for {signature_rel_path.name} failed: {latest_message}"
+            )
+        raise datatypes.FailedError(
             "Signature verification for"
             f" {signature_rel_path.name} is {latest.status.value}; cannot generate the SHA512 yet."
         )
@@ -361,9 +363,9 @@ class CommitteeParticipant(FoundationCommitter):
             resolved = await asyncio.to_thread(path_to_remove.path.resolve)
             resolved.relative_to(await asyncio.to_thread(path.path.resolve))
             if not await aiofiles.os.path.isdir(path_to_remove):
-                raise types.FailedError(f"Path '{dir_to_delete_rel}' is not a directory.")
+                raise datatypes.FailedError(f"Path '{dir_to_delete_rel}' is not a directory.")
             if await aiofiles.os.listdir(path_to_remove):
-                raise types.FailedError(f"Directory '{dir_to_delete_rel}' is not empty.")
+                raise datatypes.FailedError(f"Directory '{dir_to_delete_rel}' is not empty.")
             await aiofiles.os.rmdir(path_to_remove)
 
         try:
@@ -375,7 +377,7 @@ class CommitteeParticipant(FoundationCommitter):
                 description=description,
                 modify=modify,
             )
-        except types.FailedError as e:
+        except datatypes.FailedError as e:
             return str(e)
         return None
 
@@ -454,7 +456,7 @@ class CommitteeParticipant(FoundationCommitter):
                 raise storage.AccessError("SHA512 file already exists", status=409)
 
             if not await aiofiles.os.path.exists(signature_path_in_new_revision):
-                raise types.FailedError(
+                raise datatypes.FailedError(
                     "SHA512 generation requires a detached OpenPGP signature at"
                     f" {signature_rel_path!s}. Please upload the signature first."
                 )
@@ -556,7 +558,7 @@ class CommitteeParticipant(FoundationCommitter):
                 description=description,
                 modify=modify,
             )
-        except types.FailedError as e:
+        except datatypes.FailedError as e:
             return str(e), moved_files_names, skipped_files_names
         return None, moved_files_names, skipped_files_names
 
@@ -655,18 +657,18 @@ class CommitteeParticipant(FoundationCommitter):
             phase=sql.ReleasePhase.RELEASE_PREVIEW,
             _project=True,
             _committee=True,
-        ).demand(types.FailedError("Release preview not found for publish"))
+        ).demand(datatypes.FailedError("Release preview not found for publish"))
         storage.ensure_project_active(release.project)
         committee = release.project.committee
         if committee is None:
-            raise types.FailedError("Release has no committee - Invalid state")
+            raise datatypes.FailedError("Release has no committee - Invalid state")
         latest_revision = release.safe_latest_revision_number
         if latest_revision != task_args.revision_number:
-            raise types.FailedError("A newer revision appeared after queuing this publish task")
+            raise datatypes.FailedError("A newer revision appeared after queuing this publish task")
         try:
             internal_url = util.svn_publish_internal_url(committee, task_args.download_path_suffix)
         except ValueError as exc:
-            raise types.FailedError(f"SVN publish URL is not acceptable: {exc}") from exc
+            raise datatypes.FailedError(f"SVN publish URL is not acceptable: {exc}") from exc
         preview_path = paths.release_directory(release)
         log_message = (
             f"Publish {task_args.project_key!s}-{task_args.version_key!s}\n\n"
@@ -687,10 +689,10 @@ class CommitteeParticipant(FoundationCommitter):
                     message = f"{message}: {match.group(1)}"
                     if svn_publish_url := config.get().SVN_PUBLISH_URL:
                         message = message.replace(svn_publish_url, "")
-                raise types.FailedError(message) from exc
-            raise types.FailedError("SVN publish failed; see worker logs for details") from exc
+                raise datatypes.FailedError(message) from exc
+            raise datatypes.FailedError("SVN publish failed; see worker logs for details") from exc
         if revision is None:
-            raise types.FailedError("SVN publish did not return a Committed revision line")
+            raise datatypes.FailedError("SVN publish did not return a Committed revision line")
         return results.SvnPublish(
             kind="svn_publish",
             svn_revision=revision,
@@ -891,7 +893,7 @@ class CommitteeParticipant(FoundationCommitter):
                 description=description,
                 modify=modify,
             )
-        except types.FailedError as e:
+        except datatypes.FailedError as e:
             return str(e), renamed_count, error_messages
         return None, renamed_count, error_messages
 
@@ -1060,7 +1062,7 @@ class CommitteeParticipant(FoundationCommitter):
                 description=description,
                 modify=modify,
             )
-        except types.FailedError as e:
+        except datatypes.FailedError as e:
             return str(e), len(files), False
         return None, len(files), isinstance(result, sql.Quarantined)
 
@@ -1182,23 +1184,23 @@ class CommitteeParticipant(FoundationCommitter):
             resolved.relative_to(await asyncio.to_thread(interim_path.path.resolve))
         except ValueError:
             # Path traversal detected
-            raise types.FailedError("Paths must be restricted to the release directory")
+            raise datatypes.FailedError("Paths must be restricted to the release directory")
 
         if not await aiofiles.os.path.exists(target_path):
             for part in target_path.path.parts:
                 # TODO: This .prefix check could include some existing directory segment
                 # TODO: The second check probably isn't needed now since this came from a safe RelPath type.
                 if util.is_disallowed_dotfile(part):
-                    raise types.FailedError("This segment is a disallowed dotfile")
+                    raise datatypes.FailedError("This segment is a disallowed dotfile")
                 if ".." in part:
-                    raise types.FailedError("Segments must not contain '..'")
+                    raise datatypes.FailedError("Segments must not contain '..'")
 
             try:
                 await aiofiles.os.makedirs(target_path)
             except OSError:
-                raise types.FailedError("Failed to create target directory")
+                raise datatypes.FailedError("Failed to create target directory")
         elif not await aiofiles.os.path.isdir(target_path):
-            raise types.FailedError("Target path is not a directory")
+            raise datatypes.FailedError("Target path is not a directory")
 
         for source_file_rel in source_files_rel:
             await self.__setup_revision_item(
@@ -1226,11 +1228,11 @@ class CommitteeParticipant(FoundationCommitter):
             if (target_dir_rel == source_file_rel) or (interim_path / target_dir_path).path.resolve().is_relative_to(
                 full_source_item_path.path.resolve()
             ):
-                raise types.FailedError("Cannot move a directory into itself or a subdirectory of itself")
+                raise datatypes.FailedError("Cannot move a directory into itself or a subdirectory of itself")
 
             final_target_for_item = target_path / source_path.name
             if await aiofiles.os.path.exists(final_target_for_item):
-                raise types.FailedError("Target name already exists")
+                raise datatypes.FailedError("Target name already exists")
 
             await aiofiles.os.rename(full_source_item_path, final_target_for_item)
             moved_files_names.append(source_path.name)
@@ -1239,11 +1241,11 @@ class CommitteeParticipant(FoundationCommitter):
             bundle = [f for f in related_files if await aiofiles.os.path.exists(interim_path / f)]
             for f_check in bundle:
                 if await aiofiles.os.path.isdir(interim_path / f_check):
-                    raise types.FailedError("A related 'file' is actually a directory")
+                    raise datatypes.FailedError("A related 'file' is actually a directory")
 
             collisions = [f.name for f in bundle if await aiofiles.os.path.exists(target_path / f.name)]
             if collisions:
-                raise types.FailedError("A related file already exists in the target directory")
+                raise datatypes.FailedError("A related file already exists in the target directory")
 
             for f in bundle:
                 await aiofiles.os.rename(interim_path / f, target_path / f.name)
diff --git a/atr/storage/writers/revision.py b/atr/storage/writers/revision.py
index 4f1858c1..b8db47ee 100644
--- a/atr/storage/writers/revision.py
+++ b/atr/storage/writers/revision.py
@@ -43,7 +43,7 @@ import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.paths as paths
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.tasks as tasks
 import atr.util as util
 
@@ -185,7 +185,7 @@ async def _commit_new_revision(
         # Raise an error if the destination directory already exists
         # This can happen for example if there was a previous failed cleanup
         if await aiofiles.os.path.exists(new_revision_dir):
-            raise types.FailedError(f"Revision directory {new_revision_dir} already exists")
+            raise datatypes.FailedError(f"Revision directory {new_revision_dir} already exists")
 
         # Rename the temporary interim directory to the new revision number
         await aiofiles.os.rename(temp_dir, new_revision_dir)
@@ -344,7 +344,7 @@ async def _verify_provenance_sources_unchanged(
             except FileNotFoundError:
                 post_merge_inode = None
             if (pre_merge_inode is None) or (post_merge_inode != pre_merge_inode):
-                raise types.FailedError(
+                raise datatypes.FailedError(
                     f"A concurrent revision replaced {dependency_rel}; please regenerate {generated_rel}."
                 )
 
@@ -442,7 +442,7 @@ class CommitteeParticipant(FoundationCommitter):
             )
             storage.ensure_project_active(release.project)
             if release.phase not in allowed_phases:
-                raise types.PhaseMismatchError(
+                raise datatypes.PhaseMismatchError(
                     f"Cannot create revision: release phase is {release.phase.value}, "
                     f"allowed: {', '.join(sorted(p.value for p in allowed_phases))}"
                 )
@@ -475,7 +475,7 @@ class CommitteeParticipant(FoundationCommitter):
             # The directory is either empty or its files are hard linked to the previous revision
             if modify is not None:
                 path_provenance = await modify(temp_dir_path, old_revision)
-        except types.FailedError:
+        except datatypes.FailedError:
             await aioshutil.rmtree(temp_dir)
             raise
         except Exception:
@@ -485,7 +485,7 @@ class CommitteeParticipant(FoundationCommitter):
         validation_errors = await asyncio.to_thread(detection.validate_directory, temp_dir_path.path)
         if validation_errors:
             await aioshutil.rmtree(temp_dir)
-            raise types.FailedError("File validation failed:\n" + "\n".join(validation_errors))
+            raise datatypes.FailedError("File validation failed:\n" + "\n".join(validation_errors))
 
         # Ensure that the permissions of every directory are 755
         try:
@@ -547,7 +547,7 @@ class CommitteeParticipant(FoundationCommitter):
 
             if merged_release.phase not in allowed_phases:
                 await aioshutil.rmtree(temp_dir)
-                raise types.PhaseMismatchError(
+                raise datatypes.PhaseMismatchError(
                     f"Cannot create revision: release phase is {merged_release.phase.value}, "
                     f"allowed: {', '.join(sorted(p.value for p in allowed_phases))}"
                 )
diff --git a/atr/storage/writers/tokens.py b/atr/storage/writers/tokens.py
index fa1aae97..78b61e1f 100644
--- a/atr/storage/writers/tokens.py
+++ b/atr/storage/writers/tokens.py
@@ -32,7 +32,7 @@ import atr.log as log
 import atr.mail as mail
 import atr.models.sql as sql
 import atr.storage as storage
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 
 
 class GeneralPublic:
@@ -61,7 +61,7 @@ class FoundationCommitter(GeneralPublic):
 
     async def add_token(
         self, token_hash: str, created: datetime.datetime, expires: datetime.datetime, label: str | None
-    ) -> types.PersonalAccessTokenSafe:
+    ) -> datatypes.PersonalAccessTokenSafe:
         if not label:
             raise ValueError("Label is required")
         pat = sql.PersonalAccessToken(
@@ -83,7 +83,7 @@ class FoundationCommitter(GeneralPublic):
             "If you did not create this token, please revoke it immediately.",
         )
         await self.__write_as.mail.send(message, mail.MailFooterCategory.AUTO)
-        return types.PersonalAccessTokenSafe.from_sql(pat)
+        return datatypes.PersonalAccessTokenSafe.from_sql(pat)
 
     # audit_guidance PAT deletion revokes associated JWTs
     # audit_guidance JWT verification rechecks PAT existence on every API request
@@ -197,7 +197,7 @@ class FoundationAdmin(FoundationCommitter):
         expires: datetime.datetime,
         label: str,
         allowed_ip: str | None,
-    ) -> types.PersonalAccessTokenSafe:
+    ) -> datatypes.PersonalAccessTokenSafe:
         if not label:
             raise ValueError("Label is required")
         # asfuid stays null so a system PAT has no owning user; created_by
@@ -221,15 +221,15 @@ class FoundationAdmin(FoundationCommitter):
             allowed_ip=allowed_ip,
         )
         log.auth_event("system_pat_issuance", self.__asf_uid, pat_hash=token_hash, name=label)
-        return types.PersonalAccessTokenSafe.from_sql(pat)
+        return datatypes.PersonalAccessTokenSafe.from_sql(pat)
 
-    async def list_system_tokens(self) -> list[types.PersonalAccessTokenSafe]:
+    async def list_system_tokens(self) -> list[datatypes.PersonalAccessTokenSafe]:
         tokens = await (
             self.__data.personal_access_token(is_system=True)
             .order_by(sql.validate_instrumented_attribute(sql.PersonalAccessToken.created))
             .all()
         )
-        return [types.PersonalAccessTokenSafe.from_sql(token) for token in tokens]
+        return [datatypes.PersonalAccessTokenSafe.from_sql(token) for token in tokens]
 
     async def revoke_all_user_tokens(self, target_asf_uid: str) -> int:
         """Revoke all PATs that a specified user owns or created. Returns count of revoked tokens."""
diff --git a/atr/util.py b/atr/util.py
index a49a9217..0dc5d43c 100644
--- a/atr/util.py
+++ b/atr/util.py
@@ -63,7 +63,7 @@ import atr.registry as registry
 import atr.user as user
 
 if TYPE_CHECKING:
-    import atr.storage.types as types
+    import atr.storage.datatypes as datatypes
 
 ARCHIVE_ROOT_SUFFIXES: Final[tuple[str, ...]] = (
     "-binary-assembly",
@@ -352,7 +352,7 @@ def concern_acknowledgement_error(missing: Sequence[ConcernGroup]) -> str:
     return f"{CONCERN_ACKNOWLEDGEMENT_MESSAGE}: {', '.join(listed)}{suffix}"
 
 
-def concern_groups(info: "types.PathInfo | None") -> list[ConcernGroup]:
+def concern_groups(info: "datatypes.PathInfo | None") -> list[ConcernGroup]:
     if info is None:
         return []
     counts_by_checker: dict[str, int] = collections.defaultdict(int)
diff --git a/pyproject.toml b/pyproject.toml
index a7adc15a..e6016042 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -140,6 +140,7 @@ ignore = [
   "S704", # markupsafe
 ]
 select = [
+  "A005",  # stdlib-module-shadowing
   "ASYNC", # flake8-async
   "C90",   # cyclomatic-complexity
   "E",     # pycodestyle
@@ -154,6 +155,9 @@ select = [
   "W"
 ]
 
+[tool.ruff.lint.flake8-builtins]
+strict-checking = true
+
 [tool.ruff.lint.per-file-ignores]
 "atr/analysis.py" = ["RUF001"]
 "atr/db/__init__.py" = ["C901"]
diff --git a/scripts/keys_import.py b/scripts/keys_import.py
index 8b019161..9c4d4bdc 100755
--- a/scripts/keys_import.py
+++ b/scripts/keys_import.py
@@ -34,8 +34,8 @@ import atr.cache as cache
 import atr.config as config
 import atr.db as db
 import atr.storage as storage
+import atr.storage.datatypes as datatypes
 import atr.storage.outcome as outcome
-import atr.storage.types as types
 import atr.util as util
 
 if TYPE_CHECKING:
@@ -102,11 +102,11 @@ def format_exception_location(exc: BaseException) -> str:
     return f"{type(exc).__name__} at {filename}:{lineno} in {func}: {exc}"
 
 
-def log_outcome_errors(outcomes: outcome.List[types.Key], committee_key: str) -> None:
+def log_outcome_errors(outcomes: outcome.List[datatypes.Key], committee_key: str) -> None:
     for error in outcomes.errors():
         fingerprint = "unknown"
         detail_exception: BaseException = error
-        if isinstance(error, types.PublicKeyError):
+        if isinstance(error, datatypes.PublicKeyError):
             fingerprint = error.key.key_model.fingerprint
             detail_exception = error.original_error
         elif isinstance(error, BaseException):
diff --git a/tests/unit/test_checker_stats.py b/tests/unit/test_checker_stats.py
index 915597ac..4b8d370b 100644
--- a/tests/unit/test_checker_stats.py
+++ b/tests/unit/test_checker_stats.py
@@ -22,8 +22,8 @@ import unittest.mock as mock
 
 import atr.models.safe as safe
 import atr.models.sql as sql
+import atr.storage.datatypes as datatypes
 import atr.storage.readers.releases as releases
-import atr.storage.types as types
 
 
 def test_checker_stats_counts_and_files_by_status():
@@ -32,7 +32,7 @@ def test_checker_stats_counts_and_files_by_status():
     warning = _make_check_result(sql.CheckResultStatus.SUGGESTION, "Warning", primary_rel_path=str(path))
     failure = _make_check_result(sql.CheckResultStatus.CONCERN, "Failure", primary_rel_path=str(path))
     blocker = _make_check_result(sql.CheckResultStatus.BLOCKER, "Blocker", primary_rel_path=str(path))
-    info = types.PathInfo(
+    info = datatypes.PathInfo(
         notes={path: [success]},
         suggestions={path: [warning]},
         concerns={path: [failure]},
@@ -65,8 +65,8 @@ def test_exceptions_bucketed_into_path_info():
     path = safe.RelPath("apache-test-1.0-source.tar.gz")
     exception = _make_check_result(sql.CheckResultStatus.EXCEPTION, "Error", primary_rel_path=str(path))
     release_level = _make_check_result(sql.CheckResultStatus.EXCEPTION, "Tooling failure", primary_rel_path=None)
-    info = types.PathInfo()
-    subset = types.ChecksSubset(checks=[exception, release_level], info=info, match_ignore=lambda _: False)
+    info = datatypes.PathInfo()
+    subset = datatypes.ChecksSubset(checks=[exception, release_level], info=info, match_ignore=lambda _: False)
     reader = _make_reader()
     bucket_exceptions = getattr(reader, "_GeneralPublic__exceptions")
 
diff --git a/tests/unit/test_checks_table.py b/tests/unit/test_checks_table.py
index bdfee1f5..e893bbfc 100644
--- a/tests/unit/test_checks_table.py
+++ b/tests/unit/test_checks_table.py
@@ -21,7 +21,7 @@ import unittest.mock as mock
 import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.render as render
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 
 
 def test_highest_severity_precedence() -> None:
@@ -46,13 +46,13 @@ def test_highest_severity_precedence() -> None:
 
 
 def test_render_exception_banner_empty_returns_none() -> None:
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     assert render.render_exception_banner(info) is None
 
 
 def test_render_exception_banner_path_level() -> None:
     path = safe.RelPath("apache-test-1.0-source.tar.gz")
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     info.exceptions[path] = [_fake_check_result()]
     banner = render.render_exception_banner(info)
     assert banner is not None
@@ -63,7 +63,7 @@ def test_render_exception_banner_path_level() -> None:
 
 
 def test_render_exception_banner_release_level_only() -> None:
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     info.release_level_exceptions.append(_fake_check_result())
     banner = render.render_exception_banner(info)
     assert banner is not None
diff --git a/tests/unit/test_concern_acknowledgements.py b/tests/unit/test_concern_acknowledgements.py
index 90170702..beb4b5fa 100644
--- a/tests/unit/test_concern_acknowledgements.py
+++ b/tests/unit/test_concern_acknowledgements.py
@@ -19,12 +19,12 @@ import collections
 from types import SimpleNamespace
 
 import atr.models.sql as sql
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.util as util
 
 
 def test_concern_groups_and_missing():
-    headers_stat = types.CheckerStats(
+    headers_stat = datatypes.CheckerStats(
         checker="atr.tasks.checks.license.headers",
         counts=collections.Counter(
             {
@@ -34,18 +34,18 @@ def test_concern_groups_and_missing():
         ),
         files={sql.CheckResultStatus.CONCERN: {"a": 6}},
     )
-    paths_stat = types.CheckerStats(
+    paths_stat = datatypes.CheckerStats(
         checker="atr.tasks.checks.paths",
         counts=collections.Counter({sql.CheckResultStatus.CONCERN: 3}),
         files={sql.CheckResultStatus.CONCERN: {"a": 3}},
     )
-    suggestions_only_stat = types.CheckerStats(
+    suggestions_only_stat = datatypes.CheckerStats(
         checker="atr.tasks.checks.targz.structure",
         counts=collections.Counter({sql.CheckResultStatus.SUGGESTION: 2}),
         files={sql.CheckResultStatus.SUGGESTION: {"a": 2}},
     )
     release_level = SimpleNamespace(checker="atr.tasks.checks.signature")
-    info = types.PathInfo.model_construct(
+    info = datatypes.PathInfo.model_construct(
         checker_stats=[headers_stat, paths_stat, suggestions_only_stat],
         release_level_concerns=[release_level],
     )
diff --git a/tests/unit/test_create_revision.py b/tests/unit/test_create_revision.py
index 8add4c24..51de095e 100644
--- a/tests/unit/test_create_revision.py
+++ b/tests/unit/test_create_revision.py
@@ -24,7 +24,7 @@ import pytest
 
 import atr.models.safe as safe
 import atr.models.sql as sql
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.storage.writers.release as release
 import atr.storage.writers.revision as revision
 
@@ -403,7 +403,7 @@ async def test_modify_failed_error_propagates_and_cleans_up(tmp_path: pathlib.Pa
         received_args["path"] = path
         received_args["old_rev"] = old_rev
         (path / "file.txt").path.write_text("Should be cleaned up.")
-        raise types.FailedError("Intentional error")
+        raise datatypes.FailedError("Intentional error")
 
     release = mock.MagicMock()
     release.phase = sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT
@@ -420,7 +420,7 @@ async def test_modify_failed_error_propagates_and_cleans_up(tmp_path: pathlib.Pa
         mock.patch.object(revision.interaction, "latest_revision", new_callable=mock.AsyncMock, return_value=None),
         mock.patch.object(revision.paths, "get_tmp_dir", return_value=temp_dir),
     ):
-        with pytest.raises(types.FailedError, match="Intentional error"):
+        with pytest.raises(datatypes.FailedError, match="Intentional error"):
             await participant.create_revision_with_quarantine(
                 safe.ProjectKey("proj"),
                 safe.VersionKey("1.0"),
@@ -760,7 +760,7 @@ async def test_verify_provenance_sources_unchanged_rejects_missing_signature(tmp
             "source_paths": ["artifact.tar.gz"],
         },
     )
-    with pytest.raises(types.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz\.asc"):
+    with pytest.raises(datatypes.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz\.asc"):
         await revision._verify_provenance_sources_unchanged(
             temp_dir_path=safe.StatePath(tmp_path),
             pre_merge_inodes={
@@ -783,7 +783,7 @@ async def test_verify_provenance_sources_unchanged_rejects_missing_source(tmp_pa
             "source_paths": ["artifact.tar.gz"],
         },
     )
-    with pytest.raises(types.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz"):
+    with pytest.raises(datatypes.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz"):
         await revision._verify_provenance_sources_unchanged(
             temp_dir_path=safe.StatePath(tmp_path),
             pre_merge_inodes={"artifact.tar.gz": 123},
@@ -809,7 +809,7 @@ async def test_verify_provenance_sources_unchanged_rejects_replaced_signature(tm
             "source_paths": ["artifact.tar.gz"],
         },
     )
-    with pytest.raises(types.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz\.asc"):
+    with pytest.raises(datatypes.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz\.asc"):
         await revision._verify_provenance_sources_unchanged(
             temp_dir_path=safe.StatePath(tmp_path),
             pre_merge_inodes={
@@ -836,7 +836,7 @@ async def test_verify_provenance_sources_unchanged_rejects_replaced_source(tmp_p
             "source_paths": ["artifact.tar.gz"],
         },
     )
-    with pytest.raises(types.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz"):
+    with pytest.raises(datatypes.FailedError, match=r"concurrent revision replaced artifact\.tar\.gz"):
         await revision._verify_provenance_sources_unchanged(
             temp_dir_path=safe.StatePath(tmp_path),
             pre_merge_inodes={"artifact.tar.gz": stale_inode},
diff --git a/tests/unit/test_create_revision_quarantine.py b/tests/unit/test_create_revision_quarantine.py
index 2d10caf2..512725a6 100644
--- a/tests/unit/test_create_revision_quarantine.py
+++ b/tests/unit/test_create_revision_quarantine.py
@@ -24,7 +24,7 @@ import pytest
 
 import atr.models.safe as safe
 import atr.models.sql as sql
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.storage.writers.revision as revision
 
 _QUARANTINE_TOKEN_ALPHABET: Final[str] = "qpzry9x8gf2tvdw0s3jn54khce6mua7b"
@@ -236,7 +236,7 @@ async def test_phase_gate_rejects_mismatched_phase():
 
     with mock.patch.object(revision.db, "session", return_value=mock_session):
         with pytest.raises(
-            types.PhaseMismatchError,
+            datatypes.PhaseMismatchError,
             match="release phase is release_candidate",
         ):
             await participant.create_revision_with_quarantine(
diff --git a/tests/unit/test_keys_writer.py b/tests/unit/test_keys_writer.py
index be6359db..95122b2f 100644
--- a/tests/unit/test_keys_writer.py
+++ b/tests/unit/test_keys_writer.py
@@ -236,8 +236,8 @@ async def test_delete_key_removal_deletes_empty_keys_file(tmp_path):
 async def test_ensure_allows_key_without_apache_uid_for_bulk_import() -> None:
     data = MockData(None, committees_after_commit={})
     writer, _write_as = _make_foundation_admin(data, "alpha")
-    key = keys_writer.types.Key(
-        status=keys_writer.types.KeyStatus.PARSED,
+    key = keys_writer.datatypes.Key(
+        status=keys_writer.datatypes.KeyStatus.PARSED,
         key_model=_public_signing_key("fp1", apache_uid=None),
     )
     database_outcomes = outcome.List(outcome.Result(key))
@@ -269,8 +269,8 @@ async def test_ensure_allows_key_without_apache_uid_for_bulk_import() -> None:
 async def test_ensure_stored_one_accepts_key_with_apache_uid() -> None:
     data = MockData(None, committees_after_commit={})
     writer, _write, _write_as = _make_foundation_committer_with_audit(data)
-    key = keys_writer.types.Key(
-        status=keys_writer.types.KeyStatus.PARSED,
+    key = keys_writer.datatypes.Key(
+        status=keys_writer.datatypes.KeyStatus.PARSED,
         key_model=_public_signing_key("fp1", apache_uid="alice"),
     )
     database_outcome = outcome.Result(key)
@@ -329,8 +329,8 @@ async def test_ensure_stored_one_accepts_test_key_without_email_cache() -> None:
 async def test_ensure_stored_one_rejects_key_without_apache_uid() -> None:
     data = MockData(None, committees_after_commit={})
     writer, _write, _write_as = _make_foundation_committer_with_audit(data)
-    key = keys_writer.types.Key(
-        status=keys_writer.types.KeyStatus.PARSED,
+    key = keys_writer.datatypes.Key(
+        status=keys_writer.datatypes.KeyStatus.PARSED,
         key_model=_public_signing_key("fp1", apache_uid=None),
     )
     lookup = keys_writer.cache.EmailUidLookup({})
@@ -353,7 +353,7 @@ async def test_ensure_stored_one_rejects_key_without_apache_uid() -> None:
 
     assert isinstance(result, outcome.Error)
     error = result.error_or_none()
-    assert isinstance(error, keys_writer.types.UnknownApacheUidError)
+    assert isinstance(error, keys_writer.datatypes.UnknownApacheUidError)
     assert str(error) == "OpenPGP key could not be associated with an ASF UID. Import it through a KEYS file instead."
     email_uid_view.assert_awaited_once()
     assert block_model_create.call_count == 2
diff --git a/tests/unit/test_release_writer_hashgen.py b/tests/unit/test_release_writer_hashgen.py
index 90e262f8..5a6ddfb4 100644
--- a/tests/unit/test_release_writer_hashgen.py
+++ b/tests/unit/test_release_writer_hashgen.py
@@ -25,7 +25,7 @@ import pytest
 import atr.models.attestable as attestable
 import atr.models.safe as safe
 import atr.models.sql as sql
-import atr.storage.types as types
+import atr.storage.datatypes as datatypes
 import atr.storage.writers.release as release
 
 
@@ -155,7 +155,7 @@ async def test_generate_hash_file_requires_signature_file(tmp_path: pathlib.Path
         "_signature_provenance_metadata_for",
         new=mock.AsyncMock(),
     ) as signature_metadata_mock:
-        with pytest.raises(types.FailedError, match=r"requires a detached OpenPGP signature"):
+        with pytest.raises(datatypes.FailedError, match=r"requires a detached OpenPGP signature"):
             await participant.generate_hash_file(
                 safe.ProjectKey("proj"),
                 safe.VersionKey("1.0"),
@@ -211,7 +211,7 @@ async def test_signature_provenance_metadata_for_requires_completed_check():
         mock.patch.object(release.db, "session", return_value=session),
         mock.patch.object(release.log, "info") as log_info_mock,
     ):
-        with pytest.raises(types.FailedError, match=r"has not completed yet"):
+        with pytest.raises(datatypes.FailedError, match=r"has not completed yet"):
             await release._signature_provenance_metadata_for(
                 project_key=safe.ProjectKey("proj"),
                 version_key=safe.VersionKey("1.0"),
@@ -224,7 +224,7 @@ async def test_signature_provenance_metadata_for_requires_completed_check():
 
 @pytest.mark.asyncio
 async def test_signature_provenance_metadata_for_requires_parent_revision():
-    with pytest.raises(types.FailedError, match=r"requires a parent revision"):
+    with pytest.raises(datatypes.FailedError, match=r"requires a parent revision"):
         await release._signature_provenance_metadata_for(
             project_key=safe.ProjectKey("proj"),
             version_key=safe.VersionKey("1.0"),
@@ -248,7 +248,7 @@ async def test_signature_provenance_metadata_for_requires_successful_check():
         mock.patch.object(release.db, "session", return_value=session),
         mock.patch.object(release.log, "info") as log_info_mock,
     ):
-        with pytest.raises(types.FailedError, match=r"failed: No valid signature found"):
+        with pytest.raises(datatypes.FailedError, match=r"failed: No valid signature found"):
             await release._signature_provenance_metadata_for(
                 project_key=safe.ProjectKey("proj"),
                 version_key=safe.VersionKey("1.0"),
diff --git a/tests/unit/test_source_artifact_check.py b/tests/unit/test_source_artifact_check.py
index 6ba8dd82..ca7a5a66 100644
--- a/tests/unit/test_source_artifact_check.py
+++ b/tests/unit/test_source_artifact_check.py
@@ -25,8 +25,8 @@ import pytest
 import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.render as render
+import atr.storage.datatypes as datatypes
 import atr.storage.readers.releases as releases
-import atr.storage.types as types
 import atr.tasks.checks as checks
 import atr.tasks.checks.paths as paths
 from tests.unit.recorders import RecorderStub
@@ -50,9 +50,9 @@ async def test_binary_only_artifacts_records_failure(monkeypatch: pytest.MonkeyP
 
 @pytest.mark.asyncio
 async def test_blocker_with_path_goes_to_blockers():
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     result = _make_check_result(sql.CheckResultStatus.BLOCKER, "Bad path", primary_rel_path="some-file.tar.gz")
-    cs = types.ChecksSubset(checks=[result], info=info, match_ignore=lambda _: False)
+    cs = datatypes.ChecksSubset(checks=[result], info=info, match_ignore=lambda _: False)
     reader = _make_reader()
     await reader._GeneralPublic__blocker(cs)  # type: ignore[attr-defined]
     assert len(info.release_level_concerns) == 0
@@ -65,9 +65,9 @@ async def test_blocker_with_path_goes_to_blockers():
 
 @pytest.mark.asyncio
 async def test_blocker_without_path_goes_to_release_level_blockers():
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     result = _make_check_result(sql.CheckResultStatus.BLOCKER, "No source artifact")
-    cs = types.ChecksSubset(checks=[result], info=info, match_ignore=lambda _: False)
+    cs = datatypes.ChecksSubset(checks=[result], info=info, match_ignore=lambda _: False)
     reader = _make_reader()
     await reader._GeneralPublic__blocker(cs)  # type: ignore[attr-defined]
     assert len(info.release_level_blockers) == 1
@@ -77,9 +77,9 @@ async def test_blocker_without_path_goes_to_release_level_blockers():
 
 @pytest.mark.asyncio
 async def test_concern_without_path_goes_to_release_level_concerns():
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     result = _make_check_result(sql.CheckResultStatus.CONCERN, "Some failure")
-    cs = types.ChecksSubset(checks=[result], info=info, match_ignore=lambda _: False)
+    cs = datatypes.ChecksSubset(checks=[result], info=info, match_ignore=lambda _: False)
     reader = _make_reader()
     await reader._GeneralPublic__concerns(cs)  # type: ignore[attr-defined]
     assert len(info.release_level_concerns) == 1
@@ -235,8 +235,8 @@ async def test_no_artifacts_records_failure(monkeypatch: pytest.MonkeyPatch, tmp
 
 
 def test_render_checks_summary_emits_new_badge_classes():
-    info = types.PathInfo()
-    stat = types.CheckerStats(
+    info = datatypes.PathInfo()
+    stat = datatypes.CheckerStats(
         checker="atr.tasks.checks.paths.check_errors",
         counts=collections.Counter(
             {
@@ -255,13 +255,13 @@ def test_render_checks_summary_emits_new_badge_classes():
 
 
 def test_render_checks_summary_returns_none_when_no_errors():
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     element = render.render_checks_summary(info, safe.ProjectKey("test"), safe.VersionKey("1.0"))
     assert element is None
 
 
 def test_render_checks_summary_shows_release_level_errors():
-    info = types.PathInfo()
+    info = datatypes.PathInfo()
     result = _make_check_result(
         sql.CheckResultStatus.CONCERN,
         "Some path issue",
diff --git a/tests/unit/test_svn_publish_writer.py b/tests/unit/test_svn_publish_writer.py
index a615737f..8595e075 100644
--- a/tests/unit/test_svn_publish_writer.py
+++ b/tests/unit/test_svn_publish_writer.py
@@ -33,7 +33,7 @@ import atr.models.results as results
 import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.storage as storage
-import atr.storage.types as storage_types
+import atr.storage.datatypes as datatypes
 import atr.storage.writers.release as release_writer
 
 INTERNAL_PUBLISH_URL: Final[str] = "https://internal.example.invalid/repos/dist/atr"
@@ -129,7 +129,7 @@ async def test_publish_to_svn_execute_maps_existing_svn_path(
         await _seed_preview_release(data)
         writer = _release_writer(data)
 
-        with pytest.raises(storage_types.FailedError, match="Release file already exists in SVN"):
+        with pytest.raises(datatypes.FailedError, match="Release file already exists in SVN"):
             await writer.publish_to_svn_execute(
                 args.SvnPublish(
                     asf_uid="alice",
diff --git a/tests/unit/test_tokens_safe.py b/tests/unit/test_tokens_safe.py
index f6a3005e..cd3c4aa3 100644
--- a/tests/unit/test_tokens_safe.py
+++ b/tests/unit/test_tokens_safe.py
@@ -26,8 +26,8 @@ import atr.constants as constants
 import atr.db as db
 import atr.jwtoken as jwtoken
 import atr.models.sql as sql
+import atr.storage.datatypes as datatypes
 import atr.storage.readers.tokens
-import atr.storage.types as types
 import atr.storage.writers.tokens
 
 
@@ -161,7 +161,7 @@ def test_personal_access_token_safe_excludes_token_hash() -> None:
         last_used=None,
     )
 
-    safe = types.PersonalAccessTokenSafe.from_sql(token)
+    safe = datatypes.PersonalAccessTokenSafe.from_sql(token)
 
     assert safe.id == 5
     assert safe.asfuid == "test"
@@ -192,9 +192,9 @@ async def test_reader_pat_methods_return_safe_tokens() -> None:
     most_recent = await reader.most_recent_jwt_pat()
 
     assert len(tokens_list) == 1
-    assert isinstance(tokens_list[0], types.PersonalAccessTokenSafe)
+    assert isinstance(tokens_list[0], datatypes.PersonalAccessTokenSafe)
     assert "token_hash" not in tokens_list[0].model_dump()
-    assert isinstance(most_recent, types.PersonalAccessTokenSafe)
+    assert isinstance(most_recent, datatypes.PersonalAccessTokenSafe)
     assert most_recent is not None
     assert "token_hash" not in most_recent.model_dump()
 
@@ -253,7 +253,7 @@ async def test_writer_add_token_returns_safe_token() -> None:
         label="writer-test",
     )
 
-    assert isinstance(safe, types.PersonalAccessTokenSafe)
+    assert isinstance(safe, datatypes.PersonalAccessTokenSafe)
     assert safe.id == 101
     assert "token_hash" not in safe.model_dump()
 
@@ -296,7 +296,7 @@ async def test_writer_add_system_token() -> None:
         allowed_ip="10.0.0.0/8",
     )
 
-    assert isinstance(safe, types.PersonalAccessTokenSafe)
+    assert isinstance(safe, datatypes.PersonalAccessTokenSafe)
     assert safe.id == 101
     # System PATs have no owning user; created_by records the minting admin.
     assert safe.asfuid is None


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org