-
Notifications
You must be signed in to change notification settings - Fork 4
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
fix: edits to activity 3 and supporting lesson #21
Conversation
clean-modular-code/checks-conditionals/python-function-checks.md
Outdated
Show resolved
Hide resolved
clean-modular-code/checks-conditionals/python-function-checks.md
Outdated
Show resolved
Hide resolved
clean-modular-code/checks-conditionals/python-function-checks.md
Outdated
Show resolved
Hide resolved
clean-modular-code/checks-conditionals/python-function-checks.md
Outdated
Show resolved
Hide resolved
try: | ||
return int(value) | ||
except ValueError: | ||
print("Oops i can't process this so I will fail gracefully.") |
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.
Nit: this isn't failing gracefully, this is recovering. Both are valid, depending on what guarantees the function wants to make. But I would probably not print any message if I was able to recover from an error and the rest of the program could continue
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.
@ucodery, I appreciate this. I was actually going to ask you about this. There are two sides here (I think?). If I raise the error, then I get the full stack output. My message is at the bottom. For a lot of people, that's a lot of "stuff" to read.
If i print the error - i understand that this is a "recovery" as you call it above. from a user perspective (end user) it looks prettier / cleaner is easier to digest.
The program continues to run, which is problematic because, in this case, it will fail later because it has no data to process. From a development perspective, raising the error is nice for testing and feels more robust. it also should fail at this point for our demo workflow in this workshop because this means there is no data! But again the traceback output is a lot for a user to digest and has a lot of info that most users don't need.
i'm curious what your thoughts are here in terms of what to suggest people do - or how do they make decisions?
After working through this, I was thinking about providing an easier example check around data that throws a key error or index error . That's easier because we'd want to keep processing but do something with that data to clean it even it that something is returning None
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.
I can appreciate that printing the error message makes for a nicer intro to errors at runtime for students. I think my reaction to this is because print
is in the except block, but then no error is raised. So the exception is passively squashed, and execution continues, without a data
variable, possible causing issues later on (the opposite of "fail fast").
If you do (re)raise an exception, the student is now faced with the info dump of a stack trace as well as an error message. To avoid this, the program could sys.exit()
or in some other way terminate.
As an aside, part of the reason there is so much going on in your first example is that the code is raising a new exception inside of an except block, which triggers a chained exception. This can be very useful to debugging, but to suppress the extra information that is not helpful in this case, raise FileNotFoundError("Oops!") from None
can be used.
This has the advantage of reducing unnecessary information, but makes teaching the raise
and except
statements potentially harder.
Another strategy, which adds complication to introducing basic error handling, but gives more control over the output, is to explicitly format exceptions. For instance, there is a stdlib that lets you do
try:
with open(file_path, "r") as file:
data = file.read()
except FileNotFoundError as fe:
print("".join(traceback.format_exception_only(fe)))
sys.exit()
and there are 3rd party libraries that are meant to enhance tracebacks. Often this means adding more information, but there are ways to instead reduce the overall information (like suppressing foreign libraries).
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.
thank you @ucodery ok I think that I just learned a LOT more about exceptions. It is the chaining that I didn't fully understand. I know how to read the chains, but I didn't know about from None and about the concept of chaining. thank you.
this lesson is actually a lot more complex that I originally thought it would be!
Co-authored-by: Jeremy Paige <ucodery@gmail.com>
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.
Looking good.
@@ -220,20 +355,101 @@ print("Final shape of combined DataFrame:", all_papers_df.shape) | |||
|
|||
+++ {"editable": true, "slideshow": {"slide_type": ""}} | |||
|
|||
:::{admonition} On your own 1 | |||
## What i don't like |
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 i don't like | |
## What I don't like |
## Handling edge cases | ||
Your goal is to identify detect and data processing or workflow problems immediately when they occur, rather than allowing | ||
them to propagate through your code. This approach saves time and makes | ||
debugging easier, providing clearer, more useful error outputs (known as stack traces). |
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.
debugging easier, providing clearer, more useful error outputs (known as stack traces). | |
debugging easier, providing clearer, more useful error outputs (known as tracebacks). |
|
||
When working with messy data, you'll often encounter edge cases - unusual or unexpected data that can break your processing pipeline. Functions allow you to implement robust error handling and data validation. Here are some techniques you can use: | ||
When working with messy data, you'll often encounter edge cases - unusual or unexpected data that can break your processing pipeline. Functions allow you to implement robust error handling and data validation. |
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.
When working with messy data, you'll often encounter edge cases - unusual or unexpected data that can break your processing pipeline. Functions allow you to implement robust error handling and data validation. | |
When working with messy data, you'll often encounter unanticipated edge cases, such as unusual or unexpected data that can break your processing pipeline. Functions allow you to implement robust error handling and data validation. |
except FileNotFoundError: | ||
return f"File not found: {file_path}" | ||
except Exception as e: | ||
return f"An error occurred while reading {file_path}: {e}" |
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.
return f"An error occurred while reading {file_path}: {e}" | |
return f"An unexpected error occurred while reading {file_path}: {e}" |
There are two main approaches to handling potential errors: | ||
|
||
- **LBYL (Look Before You Leap)**: Check for conditions before making calls or | ||
accessing data. | ||
- **EAFP (Easier to Ask for Forgiveness than Permission)**: Assume the operation | ||
will succeed and handle any exceptions if they occur. | ||
|
||
Pythonic code generally favors the EAFP approach, which allows for **failing | ||
fast** when an error occurs, providing useful feedback without unnecessary | ||
checks. |
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.
I'm not sure that this is generally true since it depends on the use case, but YOLO.
Co-authored-by: Carol Willing <carolcode@willingconsulting.com>
ok merging this!! The most recent visual version is on circle ci for now. |
This is the start to revisiting concepts taught in activity 3. it's a followup of merged pr #18 I will bring several comments over from @ucodery
We want to make sure that most of the lesson content is in the lesson rather than the activity but we can then link to it in the activity.
also - this is the example provided around failing fast
I think this content could be a single workshop or even a day or two of programming on its own, but we will keep it high-level.