From 5049540fe8dc924b0883c1249fff7d6d6e2b4dfa Mon Sep 17 00:00:00 2001 From: Aliaksei Klimau Date: Wed, 22 Apr 2026 15:25:03 +0200 Subject: [PATCH] Add more Pulp Exceptions. Assisted-by: Claude Sonnet 4.6 --- CHANGES/+add-pulp-exceptions.feature | 1 + pulp_gem/app/exceptions.py | 90 ++++++++++++++++++++++++++++ pulp_gem/app/tasks/synchronizing.py | 5 +- pulp_gem/specs.py | 17 ++++-- 4 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 CHANGES/+add-pulp-exceptions.feature create mode 100644 pulp_gem/app/exceptions.py diff --git a/CHANGES/+add-pulp-exceptions.feature b/CHANGES/+add-pulp-exceptions.feature new file mode 100644 index 0000000..b0fd643 --- /dev/null +++ b/CHANGES/+add-pulp-exceptions.feature @@ -0,0 +1 @@ +Add more Pulp Exceptions. diff --git a/pulp_gem/app/exceptions.py b/pulp_gem/app/exceptions.py new file mode 100644 index 0000000..1bf745f --- /dev/null +++ b/pulp_gem/app/exceptions.py @@ -0,0 +1,90 @@ +from gettext import gettext as _ + +from pulpcore.plugin.exceptions import PulpException + + +class RemoteURLRequiredError(PulpException): + """ + Raised when a sync is attempted without a URL on the remote. + """ + + error_code = "GEM0001" + + def __str__(self): + return f"[{self.error_code}] " + _("A remote must have a url specified to synchronize.") + + +class RemoteConnectionError(PulpException): + """ + Raised when a connection to the remote host fails. + """ + + error_code = "GEM0002" + + def __init__(self, host): + self.host = host + + def __str__(self): + return f"[{self.error_code}] " + _("Could not connect to host {host}").format( + host=self.host + ) + + +class InvalidGemNameError(PulpException): + """ + Raised when a gem name does not match the expected format. + """ + + error_code = "GEM0003" + + def __init__(self, name): + self.name = name + + def __str__(self): + return f"[{self.error_code}] " + _("Invalid gem name: {name}").format(name=self.name) + + +class InvalidRequirementError(PulpException): + """ + Raised when a gem info file contains an unrecognized requirement key. + """ + + error_code = "GEM0004" + + def __init__(self, stmt): + self.stmt = stmt + + def __str__(self): + return f"[{self.error_code}] " + _("Invalid requirement: {stmt}").format(stmt=self.stmt) + + +class InvalidVersionStringError(PulpException): + """ + Raised when a gem version string does not match the expected format. + """ + + error_code = "GEM0005" + + def __init__(self, version): + self.version = version + + def __str__(self): + return f"[{self.error_code}] " + _("Invalid version string: {version}").format( + version=self.version + ) + + +class UnknownRubyClassError(PulpException): + """ + Raised when YAML parsing encounters an unknown Ruby class. + """ + + error_code = "GEM0006" + + def __init__(self, suffix): + self.suffix = suffix + + def __str__(self): + return f"[{self.error_code}] " + _("Unknown ruby class {suffix}.").format( + suffix=self.suffix + ) diff --git a/pulp_gem/app/tasks/synchronizing.py b/pulp_gem/app/tasks/synchronizing.py index 42aa71f..7e89d34 100644 --- a/pulp_gem/app/tasks/synchronizing.py +++ b/pulp_gem/app/tasks/synchronizing.py @@ -14,6 +14,7 @@ Stage, ) +from pulp_gem.app.exceptions import RemoteConnectionError, RemoteURLRequiredError from pulp_gem.app.models import GemContent, GemRemote from pulp_gem.specs import ( NAME_REGEX, @@ -44,7 +45,7 @@ def synchronize(remote_pk, repository_pk, mirror=False): repository = Repository.objects.get(pk=repository_pk) if not remote.url: - raise ValueError(_("A remote must have a url specified to synchronize.")) + raise RemoteURLRequiredError() first_stage = GemFirstStage(remote) dv = DeclarativeVersion(first_stage, repository, mirror=mirror) @@ -87,7 +88,7 @@ async def run(self): try: versions_result = await versions_downloader.run() except ClientConnectionError as e: - raise Exception(f"Could not connect to host {e.host}") + raise RemoteConnectionError(host=e.host) await pr_download_versions.aincrement() async with ProgressReport(message="Parsing versions list") as pr_parse_versions: diff --git a/pulp_gem/specs.py b/pulp_gem/specs.py index 16fb574..3fe46cf 100644 --- a/pulp_gem/specs.py +++ b/pulp_gem/specs.py @@ -14,6 +14,13 @@ import rubymarshal.writer import rubymarshal.reader +from pulp_gem.app.exceptions import ( + InvalidGemNameError, + InvalidRequirementError, + InvalidVersionStringError, + UnknownRubyClassError, +) + log = getLogger(__name__) NAME_REGEX = re.compile(r"[\w\.-]+") @@ -118,7 +125,7 @@ async def read_versions(relative_path): for name, (ext_versions, md5_sum) in results.items(): # Sanitize name if not NAME_REGEX.fullmatch(name): - raise ValueError(f"Invalid gem name: {name}") + raise InvalidGemNameError(name=name) yield name, ext_versions, md5_sum @@ -159,7 +166,7 @@ async def read_info(relative_path, versions_info): elif key == "rubygems": gem_info["required_rubygems_version"] = value else: - raise ValueError(f"Invalid requirement: {stmt}") + raise InvalidRequirementError(stmt=stmt) yield gem_info @@ -348,7 +355,7 @@ def _yaml_ruby_constructor(loader, suffix, node): try: return rubymarshal.classes.registry[suffix].yaml_constructor(loader, node) except KeyError: - raise NotImplementedError(f"Unknown ruby class {suffix}.") + raise UnknownRubyClassError(suffix=suffix) yaml.add_multi_constructor("!ruby/object:", _yaml_ruby_constructor, Loader=RubyMarshalYamlLoader) @@ -380,14 +387,14 @@ def analyse_gem(file_obj): } # Sanitize name if not NAME_REGEX.fullmatch(gem_info["name"]): - raise ValueError(f"Invalid gem name: {gem_info['name']}") + raise InvalidGemNameError(name=gem_info["name"]) # Sanitize version if VERSION_REGEX.fullmatch(gem_info["version"]): gem_info["prerelease"] = False elif PRERELEASE_VERSION_REGEX.fullmatch(gem_info["version"]): gem_info["prerelease"] = True else: - raise ValueError(f"Invalid version string: {gem_info['version']}") + raise InvalidVersionStringError(version=gem_info["version"]) for key in ("required_ruby_version", "required_rubygems_version"): if (requirement := data._private_data.get(key)) is not None: gem_info[key] = requirement.to_s()