Skip to content

Remove asserts from client library code#201

Open
sashgorokhov wants to merge 2 commits into
smart-on-fhir:mainfrom
sashgorokhov-forks:remove-asserts
Open

Remove asserts from client library code#201
sashgorokhov wants to merge 2 commits into
smart-on-fhir:mainfrom
sashgorokhov-forks:remove-asserts

Conversation

@sashgorokhov

Copy link
Copy Markdown

Using asserts in production code can be dangerous in cases where python interpreter is running with -O optimization option, which will remove all assertion statements completely from bytecode.

There multiple ways how to address this, one simplest would be is to replace assert statement with if condition that would raise an exception, making check logically equivalent.

Originally I wanted to refactor logic to make these checks unnecessary at all, but that resulted in lots of changes that snowballed out of control. I've found couple of logical loopholes that allow creating instances with broken state. Being first time contributor in this project I decided to start small first.

Speaking of loopholes, there are several:

  • FHIRServer modifies and checks base_uri only during __init__, and does not check what is provided into .from_state
  • FHIRClient does not check that app_id and api_base (or base_uri) are provided via state
  • It is possible to instantiate FHIRClient with its server being in unusable state

To address all these, I would honestly start with adding typing into the whole library, and then start adjusting any logic responsible for instance initialization and correct-state-insurance into from_state method. "state" and "from_state" can operate on TypedDict instances (ok to use since this library is python >=3.10), and through them, library can communicate which attributes are required or not.

What do you guys think? I'd be excited to help y'all with these improvements.

@sashgorokhov sashgorokhov marked this pull request as ready for review February 24, 2026 00:19

@mikix mikix left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I totally agree that this library could use a healthy injection of type checking. Easiest to start with the custom code. But also fhir-parser could benefit from it, and then have that bleed into this repo's models.

Also agreed that those loopholes would be good to close.

Comment thread AUTHORS.md
Comment on lines 12 to +13
- Alexandru Stanciu <https://github.com/ducu>
- Alexander Gorokhov <https://github.com/sashgorokhov>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is such a tiny thing, but this isn't the right alphabetical order, right? You should be at the top there.

Comment thread fhirclient/server.py
Comment on lines -185 to +187
assert self.base_uri and path
if not path:
raise ValueError(f"Parameter `path` must not be None or empty, got: {path}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You dropped the check for base_uri.

Comment thread fhirclient/client.py
Comment on lines -237 to +244
self.server = FHIRServer(self, state=state.get("server"))
base_uri = self.server.base_uri if self.server is not None else None
self.server = FHIRServer(
client=self,
base_uri=state.get('server', {}).get('base_uri') or base_uri,
state=state.get("server")
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OK so now we get into an actual behavior change 😄 You are trying to pass on the existing base_uri as a default value. I can see some sense to that, but it might be surprising that some of the old state config would survive a call to from_state. I guess it's just a fallback? So without this code, it wouldn't work anyway. But maybe that's a clearer behavior? Rather than a surprise base_uri being snuck in if you don't expect it? Did you hit the error code path yourself?

As for the code changes, assuming we do like this behavior change:

  • ignorable taste nit: You could rewrite that base_uri = line as base_uri = self.server and self.server.base_uri. (a) FHIRServer doesn't define __bool__ or __len__, so a simple truthy check is equivalent to is not None and scans better, (b) I find the "X and Y" chaining easier to read than the ternary "Y if X else None" operator.
  • actual probably should change it nit: You don't need to dig into the state dict for it's base_uri - FHIRServer.from_state will already do that for you when its init method calls it. You can just pass in base_uri=base_uri and let from_state handle the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And if we keep this change, probably worth a small unit test to confirm behavior.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hell, from a coverage perspective, the ValueErrors also deserve a unit test. Though that's another todo item - enforce coverage.

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.

2 participants