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

refactor: refactor try_network_action() (DEV-1844) #325

Merged
merged 12 commits into from Mar 23, 2023

Conversation

jnussbaum
Copy link
Collaborator

resolves DEV-1844

@jnussbaum jnussbaum self-assigned this Mar 13, 2023
@linear
Copy link

linear bot commented Mar 13, 2023

@jnussbaum jnussbaum changed the title chore: refactor try network action (DEV-1844) refactor: refactor try_network_action() (DEV-1844) Mar 22, 2023
@@ -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.")
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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)
Copy link
Collaborator Author

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

@jnussbaum
Copy link
Collaborator Author

@BalduinLandolt One thing that could be problematic is the manner how multiple-line log messages are written. It looks like this:

2023-03-21 13:47:12,248   shared.py            ERROR    The XML file contains the following syntax error: Opening and ending tag mismatch: xml line 69 and text, line 69, column 109
Traceback (most recent call last):
  File "/Users/nussbaum/Desktop/dsp-tools/src/dsp_tools/utils/shared.py", line 115, in validate_xml_against_schema
    doc = etree.parse(source=input_file)
  File "src/lxml/etree.pyx", line 3541, in lxml.etree.parse
  File "src/lxml/parser.pxi", line 1879, in lxml.etree._parseDocument
  File "src/lxml/parser.pxi", line 1905, in lxml.etree._parseDocumentFromURL
  File "src/lxml/parser.pxi", line 1808, in lxml.etree._parseDocFromFile
  File "src/lxml/parser.pxi", line 1180, in lxml.etree._BaseParser._parseDocFromFile
  File "src/lxml/parser.pxi", line 618, in lxml.etree._ParserContext._handleParseResultDoc
  File "src/lxml/parser.pxi", line 728, in lxml.etree._handleParseResult
  File "src/lxml/parser.pxi", line 657, in lxml.etree._raiseParseError
  File "testdata/xml-data/test-data-systematic.xml", line 69
lxml.etree.XMLSyntaxError: Opening and ending tag mismatch: xml line 69 and text, line 69, column 109
2023-03-21 13:47:41,226   xml_upload.py        INFO     Method call xml_upload(input_file='testdata/xml-data/test-data-systematic.xml', server='http://0.0.0.0:3333', user='root@example.com', imgdir='.', sipi='http://0.0.0.0:1024', verbose=False, incremental=False, save_metrics=False)
2023-03-21 13:47:46,894   shared.py            ERROR    XML-tags are not allowed in text properties with encoding=utf8. The following resources of your XML file violate this rule:
 - line 69: resource 'INV39A4413ADDA3452AAA8A1AC58B88D3D4', property ':hasId'
NoneType: None

By accident, I saw this today, and liked it much more than my solution:

2023-03-22 17:36:42 [INFO]   ---------------------------------------------
2023-03-22 17:36:42 [INFO]   --- GitHub Actions Multi Language Linter ----
2023-03-22 17:36:42 [INFO]    - Image Creation Date:[2023-01-04T06:59:26Z]
2023-03-22 17:36:42 [INFO]    - Image Revision:[985ef206aaca4d560cb9ee2af2b42ba44adc1d55]
2023-03-22 17:36:42 [INFO]    - Image Version:[985ef206aaca4d560cb9ee2af2b42ba44adc1d55]
2023-03-22 17:36:42 [INFO]   ---------------------------------------------
2023-03-22 17:36:42 [INFO]   ---------------------------------------------
2023-03-22 17:36:42 [INFO]   The Super-Linter source code can be found at:
2023-03-22 17:36:42 [INFO]    - https://github.com/github/super-linter
2023-03-22 17:36:42 [INFO]   ---------------------------------------------

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?

Copy link
Collaborator

@BalduinLandolt BalduinLandolt left a 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())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
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

Copy link
Collaborator Author

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

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator

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

Comment on lines +51 to +52
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...")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested 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...")
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

Comment on lines +75 to +76
err_msg = f"Cannot create project '{project_definition['project']['shortname']}' " \
f"({project_definition['project']['shortcode']}) on DSP server."
Copy link
Collaborator

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
Copy link
Collaborator

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

@jnussbaum jnussbaum merged commit 896586f into main Mar 23, 2023
2 checks passed
@jnussbaum jnussbaum deleted the wip/dev-1844-refactor-try-network-action branch March 23, 2023 10:09
@daschbot daschbot mentioned this pull request Mar 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants