Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use specific exception classes where appropriate #811

Closed
bdice opened this issue Oct 19, 2020 · 5 comments · Fixed by #1174
Closed

Use specific exception classes where appropriate #811

bdice opened this issue Oct 19, 2020 · 5 comments · Fixed by #1174
Assignees
Labels
complex A particularly complex or large project that involves significant amount of effort. enhancement New feature or request

Comments

@bdice
Copy link
Member

bdice commented Oct 19, 2020

Description

I tried to read a file that doesn't exist with sim.create_state_from_gsd. Should GSD file reading errors be OSError and/or FileNotFoundError instead of RuntimeError?

**ERROR**: dump.gsd: Bad file descriptor - lattice.gsd

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-3-3623b04ad812> in <module>
     11 
     12 sim.operations.integrator = mc
---> 13 sim.create_state_from_gsd(filename='lattice.gsd')

~/miniconda3/envs/dice/lib/python3.8/site-packages/hoomd/simulation.py in create_state_from_gsd(self, filename, frame)
    107                                         self.device._cpp_exec_conf)
    108         # Grab snapshot and timestep
--> 109         reader = _hoomd.GSDReader(self.device._cpp_exec_conf,
    110                                   filename, abs(frame), frame < 0)
    111         snapshot = Snapshot._from_cpp_snapshot(reader.getSnapshot(),

RuntimeError: Error reading GSD file

Proposed solution

Use another exception instead of RuntimeError, perhaps FileNotFoundError or OSError.

Additional context

I think this may apply to other exceptions as well - we use RuntimeError pretty liberally in our code bases and it seems like it's a catch-all for things that have more specific causes. The docstring says

Raised when an error is detected that doesn’t fall in any of the other categories. The associated value is a string indicating what precisely went wrong.

I frequently refer to this list of all built-in exceptions when choosing what to raise. It may be useful to other developers. https://docs.python.org/3/library/exceptions.html#exception-hierarchy

@bdice bdice added the enhancement New feature or request label Oct 19, 2020
@vyasr
Copy link
Contributor

vyasr commented Oct 20, 2020

While I agree that HOOMD probably overuses RuntimeError, I think the underlying issue in many cases (including this example) is not in Python, but rather in C++ code that throws runtime_error, which gets transparently mapped to Python's RuntimeError. Fixing this more thoroughly will require using different C++ exceptions. Fortunately, in addition to mapping a number of built-in C++ exceptions to corresponding Python exceptions, pybind11 also defines additional exceptions to map to otherwise inaccessible Python exceptions from C++, and it even provides a mechanism for mapping custom C++ exceptions to Python ones. While the custom exception mapping is probably overkill for our purposes, making more judicious choices of exceptions in the C++ code (including some of the built-in pybind11 exceptions) would probably be quite helpful. A simple search for runtime_error in C++ headers and sources should identify the places to be checked.

@joaander
Copy link
Member

pybind11 does not define any default mapping to OSError or FileNotFoundError: https://pybind11.readthedocs.io/en/stable/advanced/exceptions.html We will need to use custom exceptions to throw these.

@joaander joaander changed the title GSD file reading exception Use specific exception classes where appropriate Oct 21, 2020
@joaander
Copy link
Member

There is another patten we should fix when we work on this issue. In most places, HOOMD issues an error and then raises an exception. Example:

        m_exec_conf->msg->error() << "Requesting type name for non-existent type " << type << endl;
        throw runtime_error("Error mapping type name");

Instead, this should not call the error and put the descriptive string in the exception. I've been using ostringstream for this when I update it, but it is cumbersome. Is there a better way? Example:

        std::ostringstream s;
        s << "No supported GPUs are present on this system." << std::endl;
        for (const auto& msg : s_gpu_scan_messages)
            s << msg << std::endl;
        throw runtime_error(s.str());

@bdice
Copy link
Member Author

bdice commented Oct 21, 2020

@joaander Changing that "log error and except" pattern would be a great idea. I think std::ostringstream is about as good as you can get. There are some other suggestions here but I support the approach you took in that snippet above.

@joaander joaander added the complex A particularly complex or large project that involves significant amount of effort. label Jul 21, 2021
@joaander
Copy link
Member

I have made slow progress in converting the error, then throw calls to just a throw with the full error description as outlined above. There are still many places in the codebase to check and convert. We should make a concerted effort to convert them all over before releasing 3.0.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
complex A particularly complex or large project that involves significant amount of effort. enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants