Skip to content

refactor: replace ValueError and KeyError with custom exception types#385

Open
jinukuntlaakhilakumargoud-web wants to merge 6 commits intoPowerGridModel:mainfrom
jinukuntlaakhilakumargoud-web:fix-generic-errors
Open

refactor: replace ValueError and KeyError with custom exception types#385
jinukuntlaakhilakumargoud-web wants to merge 6 commits intoPowerGridModel:mainfrom
jinukuntlaakhilakumargoud-web:fix-generic-errors

Conversation

@jinukuntlaakhilakumargoud-web
Copy link

@jinukuntlaakhilakumargoud-web jinukuntlaakhilakumargoud-web commented Mar 2, 2026

Closes #384

This PR implements the custom error types discussed in Issue #384.

Replaced generic ValueError and KeyError exceptions with specific, custom-built exception types in the conversion module.
Introduced exceptions.py featuring:

  • ComponentAlreadyExistsError(ValueError)
    • ComponentNotFoundError(KeyError)
    • InvalidComponentTypeError(ValueError)
    • InvalidDataFormatError(ValueError)

Copy link
Member

@mgovers mgovers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @jinukuntlaakhilakumargoud-web,

Thank you for your contribution! Overall, it seems to be in a good state, but I will not be able to do a full review today. Here's some initial feedback on a couple things that caught my eye.

Hope that helps.

@mgovers mgovers added the improvement Existing functionality, but better, faster, stronger label Mar 2, 2026
@mgovers mgovers changed the title refactor: replace ValueError and KeyError with custom exception types (closes #384) refactor: replace ValueError and KeyError with custom exception types Mar 2, 2026
@furqan463
Copy link
Contributor

Need to update tests as well, as the tests are currently expecting ValueError etc.

@jinukuntlaakhilakumargoud-web
Copy link
Author

Hi @mgovers and @furqan463,

Thank you for the feedback! I have pushed a new commit that addresses all the points:

  1. Removed the custom exception exports from __init__.py.
    1. Renamed exceptions.py to errors.py.
    1. Updated the tests in test_tabular_converter.py to expect the new custom exception types (InvalidComponentTypeError, ComponentNotFoundError, InvalidDataFormatError) where appropriate, replacing the old ValueError and KeyError assertions.
      All 749 tests are now passing locally with 98% coverage! Let me know if anything else is needed.

@furqan463
Copy link
Contributor

Hi @mgovers and @furqan463,

Thank you for the feedback! I have pushed a new commit that addresses all the points:

  1. Removed the custom exception exports from __init__.py.
    1. Renamed exceptions.py to errors.py.
    1. Updated the tests in test_tabular_converter.py to expect the new custom exception types (InvalidComponentTypeError, ComponentNotFoundError, InvalidDataFormatError) where appropriate, replacing the old ValueError and KeyError assertions.
      All 749 tests are now passing locally with 98% coverage! Let me know if anything else is needed.

Great, and please make signed commits to avoid DCO failure.

…closes PowerGridModel#384)

Signed-off-by: jinukuntlaakhilakumargoud-web <jinukuntlaakhilakumargoud@gmail.com>
- Updated tests in test_tabular_converter to expect InvalidDataFormatError and InvalidComponentTypeError
- Un-exported custom exception classes from __init__.py as per maintainer feedback
- Renamed exceptions.py to errors.py to prevent potential shadowing
- Reverted NotImplementedError replacements in test_serialize_data

Signed-off-by: jinukuntlaakhilakumargoud-web <jinukuntlaakhilakumargoud@gmail.com>
@jinukuntlaakhilakumargoud-web
Copy link
Author

@furqan463 Done! I've rebased the commits and added the Signed-off-by line to fix the DCO failure. Force-pushed the updates, so the DCO checks should be passing now.

@jinukuntlaakhilakumargoud-web
Copy link
Author

I have addressed all the comments:

  • Replaced generic errors with InvalidDatasetTypeError, MappingNotFoundError, and AttributeNotFoundError in the conversion module.
    • Reverted unintended changes in src/power_grid_model_io/__init__.py.
    • Reverted the functional change in tabular_converter.py (line 825) to avoid modifying the logic.
    • Verified all unit tests locally (297 passed).
    • Fixed the DCO failure by amending the commits with -s.
      Please let me know if any further changes are needed.

@furqan463
Copy link
Contributor

I have addressed all the comments:

  • Replaced generic errors with InvalidDatasetTypeError, MappingNotFoundError, and AttributeNotFoundError in the conversion module.
    • Reverted unintended changes in src/power_grid_model_io/__init__.py.
    • Reverted the functional change in tabular_converter.py (line 825) to avoid modifying the logic.
    • Verified all unit tests locally (297 passed).
    • Fixed the DCO failure by amending the commits with -s.
      Please let me know if any further changes are needed.
  1. Run pre-commit to fix code quality issues
    uv run pre-commit run --all
  2. Copy licence information from any of the .py file and put it at top of the errors.py file to fix reuse compliance.

Signed-off-by: jinukuntlaakhilakumargoud-web <jinukuntlaakhilakumargoud@gmail.com>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these auto-generated files should not be present in the git repo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that! I've removed the auto-generated diagnostic files (import_diagnostic.txt and export_diagnostic.txt) from the repository and updated .gitignore so they aren't accidentally committed again. The changes have been pushed to the PR!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jinukuntlaakhilakumargoud-web text files are still there.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@furqan463 You're right, I accidentally committed those test_collect_output*.txt files. I have removed them now, so no extra text files should remain.

Signed-off-by: jinukuntlaakhilakumargoud-web <jinukuntlaakhilakumargoud@gmail.com>
@furqan463
Copy link
Contributor

@jinukuntlaakhilakumargoud-web no need to add these to .gitignore.
instead, delete the text files and then commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did the line endings change? were they \r\n before instead of plain \n (in that case, you can keep it as-is) or did you change them to \r\n?

Copy link
Author

@jinukuntlaakhilakumargoud-web jinukuntlaakhilakumargoud-web Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed the line endings back to \n. Thanks for catching that.

.gitignore Outdated
docs/Makefile

# Ignore diagnostic output
*diagnostic.txt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

were the test_collect_output files auto-generated or did you manually add them yourself? if the former, please also add test_collect_output*.txt here. If the latter, please do not add it here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test_collect_output*.txt files were manually generated when I was redirecting test output and were accidentally committed. I have removed them from the repository entirely and reverted the change to .gitignore that added them

Copy link
Member

@figueroa1395 figueroa1395 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly nitpicks. Thanks for the contribution!

Comment on lines +39 to +40
# Ignore diagnostic output
*diagnostic.txt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is very specific to your workflow. Can you remove it and just manually delete or not commit these files?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the feedback. I've removed the diagnostic file entries from .gitignore.



class ComponentNotFoundError(KeyError, PowerGridModelIoError):
"""Raised when a required component or attribute is missing."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's separate components from attributes.

Suggested change
"""Raised when a required component or attribute is missing."""
"""Raised when a required component is missing."""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I've updated the docstring to only refer to components, as suggested.

Comment on lines +32 to +33
class AttributeNotFoundError(ComponentNotFoundError):
"""Raised when an attribute is missing from a component/dataset."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With reference to https://github.com/PowerGridModel/power-grid-model-io/pull/385/changes#r2883076143. Let's keep the attribute related error independent:

Suggested change
class AttributeNotFoundError(ComponentNotFoundError):
"""Raised when an attribute is missing from a component/dataset."""
class AttributeNotFoundError(KeyError, PowerGridModelIoError):
"""Raised when a required component attribute is missing."""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestion. I've updated AttributeNotFoundError to inherit from KeyError, PowerGridModelIoError directly.



class InvalidDatasetTypeError(ValueError, PowerGridModelIoError):
"""Raised when an invalid dataset type (e.g. input/output) is encountered."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Raised when an invalid dataset type (e.g. input/output) is encountered."""
"""Raised when an invalid dataset type (e.g. other than input, output, or update) is encountered."""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the docstring as suggested. Thank you.

Comment on lines +205 to +207
raise InvalidDatasetTypeError(
f"Invalid component type '{component_str}' or data type '{data_type_str}'"
) from ex
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exception lies in between InvalidDatasetTypeError and InvalidComponentTypeError. Can you split it?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've split the exception handling to raise InvalidDatasetTypeError for invalid data types and InvalidComponentTypeError for invalid component types separately.


if ComponentType.line in self.pgm_input_data:
raise ValueError("Line component already exists in pgm_input_data")
raise ComponentAlreadyExistsError("Line component already exists in pgm_input_data")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing all the repetition here for the messages when raising ComponentAlreadyExistsError, maybe it's a good idea if we could do something like:

Suggested change
raise ComponentAlreadyExistsError("Line component already exists in pgm_input_data")
raise ComponentAlreadyExistsError("Line", "pgm_input_data")

By modifying ComponentAlreadyExistsError to something along the lines of:

class ComponentAlreadyExistsError(ValueError, PowerGridModelIoError):
    def __init__(self, component: str, dataset: str):
        super().__init__(f"'{component}' component already exists in {dataset}")

I didn't recommend using ComponentType because that would probably be a breaking change, but please take a look.

Similar suggestion applies below in this file.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion. I've updated ComponentAlreadyExistsError to accept component and dataset arguments, and updated all call sites accordingly.

key = (pp_table, name)
if key in self.idx_lookup:
raise KeyError(f"Indexes for '{key}' already exist!")
raise ComponentAlreadyExistsError(f"Indexes for '{key}' already exist!")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this one belongs to this error type.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I've introduced IndexAlreadyExistsError for this case instead.

return {"table": table, "name": name, "index": indices[pgm_id]}
return {"table": table, "index": indices[pgm_id]}
raise KeyError(pgm_id)
raise ComponentNotFoundError(pgm_id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither does this one. Perhaps this one and the above can be put into something like IndexToComponentNotFoundError or something similar.

My suggestion is a bit long, but that's the idea.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestion. I've introduced IndexToComponentNotFoundError for this case.

obj_id = obj["id"]
if not isinstance(obj_id, int):
raise ValueError(f"Invalid 'id' value for {component.value} {data_type.value} data")
raise InvalidDataFormatError(f"Invalid 'id' value for {component.value} {data_type.value} data")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one perhaps belongs to what I suggested in https://github.com/PowerGridModel/power-grid-model-io/pull/385/changes#r2883220795 instead.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. I've kept InvalidDataFormatError for this case as it relates to data format validation rather than component type issues.

…et) signature and fix test assertions

- Updated all ComponentAlreadyExistsError calls in pandapower_converter.py to use the new (component, dataset) constructor signature
- Replaced ValueError with ComponentAlreadyExistsError in _pp_load_elements_output
- Updated pytest.raises assertions in test_pandapower_converter_input.py and test_pandapower_converter_output.py to use ComponentAlreadyExistsError without match arguments
- All 207 pandapower converter tests pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Existing functionality, but better, faster, stronger

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[IMPROVEMENT] Create custom error types for better error handling.

4 participants