-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Change Errno into a hierarchy of OSError subclasses #5003
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the kind of change that means to abstract system details and hide them to the developer, but those details are very hard to hide, and in the case of Errno or WinError: impossible to.
For example moving Errno to Crystal::System means that developers musn't use Errno... but developers may need to access Errno in perfectly valid scenarios, for example when writing C bindings that for libraries that will set errno.
Same for WinError, which by-the-way should use the windows naming (WinError) not something else (WindowsError).
If we're abstracting the platform error systems, then mapping WinError -> Errno should be replaced with a mean to raise crystal exceptions.
Last but not least I really don't like the chosen python inspired naming. I'd really suggest to have per namespace exceptions, for example System::Error instead of OSError, or File::Not found, IO::BrokenPipe, ... that is, names that fit better in Crystal land.
spec/std/http/server/server_spec.cr
Outdated
Errno.value = @value | ||
raise Errno.new "..." | ||
Crystal::System::Errno.value = @value | ||
raise OSError.create "..." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What?! Using Crystal::System::Errno
makes no sense: it's not a platform abstraction :/
Using a ... error message is also, huh...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Furthermore pushing Errno
as Crystal::System::Errno
means that developers of C bindings will now need to use Crystal::System
... but developers musn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well with the namespace I guess I see what you mean. But Crystal cannot provide access to errno
in a cross-platform way because it's a UNIX-specific thing, and at the same time, Crystal::System::Errno
is, in fact, a platform abstraction (different POSIX platforms). What do you suggest?
By the way, developers technically don't have to access Crystal::System::Errno
, they can just use OSError.create.errno
, but they usually shouldn't need to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworked this. But now accessing errno
is a compilation error on non-POSIX.
at least I think so...
I agree with the spirit of this change. Every other high-level programming language (except Ruby) abstracts Errno with meaningful errors (for example |
@ysbaddaden, I renamed the exceptions slightly.
Strongly disagree that naming in Windows headers should in any way affect naming in Crystal. |
This is a great change, should make life a lot easier. Nice work @oprypin I do like @ysbaddaden's idea of namespacing the errors, but I don't think they belong directly in the namespace. Perhaps a good convention would be to have an |
I agree with @ysbaddaden that |
@RX14, that last sentence is definitely disjointed from reality. OSError is special in the way that any system call can produce any of them. Scattering them across the standard library will not help anyone, and is a huge source of bikeshedding. |
Please present a concrete agreed upon set of names, and I can accept it |
@oprypin Except not all syscalls produce all errnos? If the same errno has a different enough semantic meaning in two different syscalls it should become 2 different crystal error types. I am in favour of complete abstraction. If people want to think in terms of errnos, they can simply catch |
@RX14, as I've said before, if we are relying on syscalls at all, it's an extreme amount of work trying to predict all possible error codes for every case. There would still probably need to be a fallback error anyway. |
In the next 2 days I will not be able to edit the code of this pull request, but I welcome comments that have a concrete idea of implementation, not just pointing out a problem. It would be great to get an agreement on the naming of exceptions, so maybe someone can suggest a complete set of names and people can vote on that. Also tagging @txe -- maybe you have some input. |
This PR provides a sane default for every possible case without any work needed. If some undesired behavior is detected, it is easy to add a check on a case-by-case basis and manually create a different exception type (and even keep the original |
I agree - this is a massive improvement - but i'm always wary of merging imperfect improvements because I'm scared they'll become "good enough" to tag 1.0 and then we'll never get a chance to improve them further. I think that's unreasonable of me. I'll add improving this to the roadmap so we don't forget it before 1.0. |
I suppose there is a misunderstanding of what @RX14 is trying to say. I'll try to say the same in other words. |
I think is that syscallls should raise something like |
Please note that I previously proposed a solution to have per-errno exception classes, which was rejected partly because it noticeably impacted the compiler performance, despite however helpful and nicer it was to raise and catch specific errno. Compiler performance should be verified here too. @oprypin bloating the global or OSError namespace with lots of exception classes is a bad design. If we don't care about Errno and want to introduce cross platform intelligible namings it MUST be done in a logical API design. As RX14 suggests, how would we design and name the exception if we didn't make a syscall? Would we introduce a ::FileNotFoundError? Or would we introduce a File::NotFound? Otherwise, it's just hiding some well-known naming (Errno, WinError) for another naming (OSError), it's introducing a bad API design, ... That being said, we can't catch and verify each possible errno value after each syscall, this is tedious and will bloat the stdlib, but we can check the most important one(s) for each syscall, even maybe have a generic exception raiser for a namespace, and default to a generic exception for other values. Last but not least, I wouldn't hide |
First of all, this theoretically does not happen, but if it does, you reuse the appropriate OSError subclass.
So what's not logical here? Python devs spent a lot of time designing this hierarchy; it's not a historic leftover.
It's not really cross-platform. Also it's not hidden by this PR.
That's what is happening but the namespace is OSError. Can't be anything else because then
Might as well move on to ad hominem. Not sure if those positive votes reflect that though |
I hope the last weeks have given some time to look at this discussion with some distance. @oprypin I don't think @ysbaddaden is in any way aggressive against you. If your proposal was (in part) criticized as "bad design" it was done so with reason. Still, others can have different opinions and I think we should talk about arguments for both. As I understand it, both approaches have good points and at the current time I couldn't make out a conclusive argument about preferring one over the other. I think it's worth taking a look at the reasoning behind Pythons exception hierarchy. But it is worth noting that this proposal was based on an existing hierarchy with flaws. They couldn't design the best API they could think of, but had to avoid any radical changes which would break compatibility considerably. Some general thoughts on naming exceptions:
Looking at the current and proposed hierarchies I wonder about how to align things together: There is Many of the error types introduced by this PR classify as IO errors - independent of how they are named. If it is Has anyone thought about this? Or am I in some way mistaken? |
A random idea passing by: OSError should be a module Also: isn't reading the global "last OS error" variable horribly breaking thread safety? |
I agree, the current exception hierarchy is badly cobbled together and has very clearly grown over time with little design.
|
All actual errors should end with Error -- and that's really all of the exceptions that you normally think about. Again I really like what Python does. The only non-
But Python also prevents you from accidentally catching those because they derive from |
@RX14 @straight-shoota @ysbaddaden ping on this? Would love to get the errorno away |
I think this is basically rejected |
While this PR has received mixed feedback, I think everyone agrees on the principal idea to replace the generic This PR is already pretty much cluttered, outdated and probably won't be merged anyway. So, maybe we should take a step back and continue discussion in a new issue about exception design instead of this PR. But I think we have made some promising progress and can continue this matter on a good basis. #5566 should maybe be resolved first - or included in a general discussion about Crystal's exception hierarchy? |
woops |
Initial exception hierarchy is:
These are based on what Python does.
The best showcase of improvement is src/ecr/process.cr.
Note that I did not get rid of the inherent dependency on
errno
/GetLastError
, because it's just not viable to predict every possible error from those countless error codes, every time an OS API is called.OSError.create
substitutes the exception class with some subclass ofOSError
, when such a class is defined, or otherwise the genericOSError
(just likeErrno
before) with the error code available. This is also what Python does.Windows errors are integrated into this; see windows_error.cr.