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