-
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
refactor: refactor try_network_action() (DEV-1844) #325
Conversation
@@ -251,7 +251,7 @@ def _stash_circular_references( | |||
stashed_resptr_props[res][link_prop].append(str(value.value)) | |||
link_prop.values.remove(value) | |||
else: | |||
logger.exception("ERROR in remove_circular_references(): link_prop.valtype is neither text nor resptr.") | |||
logger.error("ERROR in remove_circular_references(): link_prop.valtype is neither text nor resptr.") |
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.
logging.exception() is a shorthand for logging.error(exc_info=True), which should only be called inside an except block.
proj_context = try_network_action(lambda: ProjectContext(con=con)) | ||
except BaseError: | ||
logger.error("Unable to retrieve project context from DSP server", exc_info=True) | ||
raise UserError("Unable to retrieve project context from DSP server") from 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.
Until now, the error "Unable to retrieve project context from DSP server" escalated uncontrolled. Now, it is logged incl. stacktrace, and a user-friendly UserError is raised instead
try_network_action(failure_msg="Unable to login to DSP server", action=lambda: con.login(user, password)) | ||
proj_context = try_network_action(failure_msg="Unable to retrieve project context from DSP server", | ||
action=lambda: ProjectContext(con=con)) | ||
con = login(server=server, user=user, password=password) |
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.
shared.login() is a convenience method that creates a Connection object and makes the login
@BalduinLandolt One thing that could be problematic is the manner how multiple-line log messages are written. It looks like this:
By accident, I saw this today, and liked it much more than my solution:
I'm motivated to look for a solution how multi-line messages can be split up, and every line logged separately, so that the layout is more beautiful. What do you think about that? |
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.
Overall looking good. I remarked some minor things.
I personally don't like the duplication of the error messages all over the place. I think I would prefer the pattern
msg = "..."
print(msg)
log.error(msg)
Regarding how your logs look. I would definitely leave it as is! This is exactly how multi-line logs should be looking (even though I understand your dislike for the aesthetics of it).
But when you have something like the counter example you showed, that's IMO actually bad practice, because there you have multiple log messages for one bit of information, some of them even containing only --------------
. Imagine a service like DataDog that parses your logs for you - there you would then have to look at multiple log messages, to see what's going on.
print(f"\tWARNING: Project '{project_remote.shortname}' ({project_remote.shortcode}) already exists on the DSP " | ||
f"server. Updating it...") | ||
success = False | ||
project_remote: Project = try_network_action(lambda: project_local.read()) |
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.
project_remote: Project = try_network_action(lambda: project_local.read()) | |
project_remote: Project = try_network_action(project_local.read) |
This is beside the topic, but just because it caught my eye: I'm not 100% confident, but I think you don't need the lambda here, if you just pass the method as a function
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 I introduced this, I made experiments and I found out that the lambda is necessary. It also makes sense from a theoretical point of view:
try_network_action(project_local.read)
passes the result of read()
to the method, which is exactly what we don't want.
On the other hand, try_network_action(lambda: project_local.read())
passes the read()
method itself to the method, and that's what we want
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.
no, as far as I know, the difference between
try_network_action(project_local.read)
and
try_network_action(project_local.read())
should be that one passes the function and the other the evaluation of the function - and the lambda is also just a function pointing to project_local.read
.
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.
Ah, now I see, you have omitted the ()
in your first comment. Sorry, I wasn't attentive enough.
That's actually a good point. You're probably right and I could try that out. Now, I already merged, but I will keep it in mind.
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.
no problem. Sure, give it a try at some point, if you like... but this wasn't scope of the present PR anyways, and it works the way it is, so really no rush to change
print(f"\tWARNING: Project '{project_remote.shortname}' ({project_remote.shortcode}) already exists on the DSP server. Updating it...") | ||
logger.warning(f"Project '{project_remote.shortname}' ({project_remote.shortcode}) already exists on the DSP server. Updating it...") |
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.
print(f"\tWARNING: Project '{project_remote.shortname}' ({project_remote.shortcode}) already exists on the DSP server. Updating it...") | |
logger.warning(f"Project '{project_remote.shortname}' ({project_remote.shortcode}) already exists on the DSP server. Updating it...") | |
msg = f"Project '{project_remote.shortname}' ({project_remote.shortcode}) already exists on the DSP server. Updating it..." | |
print(f"\tWARNING: {msg}") | |
logger.warning(msg) |
just a thought, how to make this duplication a bit nicer
err_msg = f"Cannot create project '{project_definition['project']['shortname']}' " \ | ||
f"({project_definition['project']['shortcode']}) on DSP server." |
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 would personally put things like {project_definition['project']['shortname']}
in a separate variable. It adds some extra lines but I think the code would still be more readable.
|
||
Args: | ||
server: URL of the DSP server to connect to | ||
user: Username (e-mail) | ||
password: Password of the user | ||
|
||
Raises: | ||
BaseError if the login fails | ||
UserError if the login fails permanently |
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 do you mean by "fails permanently"? I guess you want to stress that it failed despite multiple retries. But I feel just "fails" is clear enough
resolves DEV-1844