FIREFLY-1331: Add Version validation for compatibility check between python client and firefly server#79
Conversation
- Introduced _server_compat.py for managing server version compatibility. - Implemented version retrieval and compatibility checking in FireflyClient. - Updated pyproject.toml to include packaging dependency.
… validation logic
And update documentation and pyproject.toml for pytest
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
|
|
||
| [project.optional-dependencies] | ||
| tests = [ | ||
| "pytest", |
There was a problem hiding this comment.
this is an optional dependency for tests so won't impact end user.
| "websocket-client", | ||
| "requests" | ||
| "requests", | ||
| "packaging", |
There was a problem hiding this comment.
packaging is one of the core utilities developed and maintained by python packaging authority (pypa): https://github.com/pypa/packaging. It's also used by pip itself internally so in most cases, it is already installed in user's python environment.
| def is_server_compatible(server_version: str|None) -> bool: | ||
| if not server_version: | ||
| return False | ||
| version = standardize_version(server_version) | ||
| return version is not None and version >= _MIN_VERSION |
There was a problem hiding this comment.
I am concerned that this might not be the best way to validate the version. This look like just a string compare. I think you need to separate the major and the minor the do something similar to BrowserInfo.js. That way you can ignore the type of build.
There was a problem hiding this comment.
It's not a string compare, it's comparing two packaging.Version objects (standardize_version converts firefly version string to Version object) which knows how to handle relational operators.
You can look at the unit tests to confirm if it's missing any of the cases you are concerned about: https://github.com/Caltech-IPAC/firefly_client/blob/FIREFLY-1331-version-validation/tests/test_server_compat.py#L31-L49
There was a problem hiding this comment.
ok, I am going to probably need an overview of how this works. Maybe we can do that on Monday.
There was a problem hiding this comment.
You can read each test case as: ver >= min_ver = bool. So:
_FIXED_MIN_VERSION = standardize_version('2026.1-DEV')
('2026.1-PRE-3', True), # pre-release of same version cycle
('2026.1', True), # formal release >= minimumbecomes:
'2026.1-PRE-3' >= '2026.1-DEV' = True
'2026.1' >= '2026.1-DEV' = True
and so on
There was a problem hiding this comment.
Yes, I can demo it on Monday too.
robyww
left a comment
There was a problem hiding this comment.
Overall I think code is very clean and works well. I have several comments and would like some small changes.
Changes
- treat
reachable : falseas 2025.5 and test accordingly - only compare with major and minor. Ignore revision or build type (dev, pre), i.e. a 2026.1 pre should be treated as 2026.1
- set the
MIN_SERVER_VERSIONto a truly minimally compatible version.
Other comments
- Bringing the another packages seem like a little overkill. More packages, even if they are commonly used, always add some complexity. Maybe a package would be helpful is our comparison was more complex but I don't think it needs to be.
- I think the comparison could have been a few lines of code in
fc_utils.py. I am not sure having a_server_compat.pyis necessary. - I am glad you implemented the test
| self.url_cmd_service = urljoin(f'{protocol}://{self.location}/', 'CmdSrv/sync') | ||
| self.url_browser = urljoin(urljoin(f'{protocol}://{self.location}/', html_file), '?__wsch=') |
| server_response = self.session.get(version_url, headers=self.header_from_ws) | ||
|
|
||
| reachable = server_response.status_code == 200 | ||
| server_version = get_server_version(server_response.json()) if reachable else None |
There was a problem hiding this comment.
I think it would be better to return a dic
'reachable': Falseto keep the response consistent.
|
|
||
| ver = self._confirm_version() | ||
| if not ver['reachable']: | ||
| warn(f'Could not retrieve version of the Firefly server {url}. Proceeding without compatibility check.') |
There was a problem hiding this comment.
We are going to get this a lot at first. i think we need to include this in our cases.
Not reachable is 2025.5 or earlier. I think when it is not reachable you need to assign the server version to be 2025.5 and do the computation like that. If FireflyClient requires 2026.1 then it should be marked as not compatable otherwise is should be compatable.
| # Minimum version of Firefly server that this version of firefly_client is compatible with | ||
| # Must be updated when a new release of firefly_client is made that relies on updates in Firefly server. | ||
| # For DEV version of Firefly server, the branch and commit info isn't considered in comparison so can be omitted. | ||
| MIN_SERVER_VERSION = '2026.1-DEV' # minimum bound on DEV includes PRE and formal releases |
There was a problem hiding this comment.
unless you have could reason, it probably should be set to what ever is truly minimally compatable. 2025.5? 2025.4?
|
I just found a significant problem. You might want to correct this on the service call. It is a little bit tricky. They problem is this. Firefly apphttps://fireflydev.ipac.caltech.edu/firefly/CmdSrv/sync?cmd=CmdVersion {
"Version": "2026.1-DEV:HEAD_2982",
"Built On": "Fri Apr 17 07:02:49 UTC 2026",
"Git commit": "2982d59e8"
}Rubin Portalhttp://localhost:8080/suit/CmdSrv/sync?cmd=CmdVersion {
"Version": "v2026.1",
"Built On": "Fri Apr 17 12:42:11 MDT 2026",
"Git commit": "96dd01c",
"Firefly Library Version": "2026.1-DEV_2982",
"Firefly Git Commit": "2982d59e8"
}IrsaViewerhttps://irsadev.ipac.caltech.edu/irsaviewer/CmdSrv/sync?cmd=CmdVersion {
"Version": "v1.0",
"Built On": "Fri Apr 17 07:07:55 UTC 2026",
"Git commit": "cfe6ade6",
"Firefly Library Version": "2026.1-DEV:HEAD_2982",
"Firefly Git Commit": "2982d59e8"
}My suggestion (java side) is to modify versionInfo.add(new KeyVal<>("Version", getFireflyVersionStr(v)));
versionInfo.add(new KeyVal<>("Application Version", getVersionStr(v)));Both keys should always be added. Then take out line 139-140 if (v.getMajor()>0)
versionInfo.add(new KeyVal<>("Firefly Library Version", getFireflyVersionStr(v))); |
- Switch to new endpoint payload shape: success flag + data['Firefly Version'] - Unknown/unreachable versions pass through as compatible for backward compat - Set MIN_SERVER_VERSION to '2025.4'; add dependency log to guide future bumps - Update release procedure: only bump on API-breaking server changes Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
|
@robyww I updated PR to address following:
If version endpoint is not reachable, the server version is now returned as None which now falls back to compatible: True.
Done. Removed
I digged through past release notes and PR logs and found firefly_client PRs that depend on firefly PR. I added them to comment where |
Fixes FIREFLY-1331
FireflyClientinitialization — raisesValueErrorif the server version is below the required minimum, warns and proceeds if the version endpoint is unreachable_server_compat.pymodule with version parsing logic, which also handles Firefly's non-standard version strings (DEV with branch/commit suffix, PRE releases)tests/test_server_compat.py) and a dev test notebook (examples/development_tests/test-version.ipynb)Also see Firefly PR: Caltech-IPAC/firefly#1936
Extra:
pytestas an optional[tests]dependency; updated developer docs with instructions to run testsurl_cmd_servicefromsticky/CmdSrvtoCmdSrv/syncwhich seems more modernTesting
examples/development_tests/test-version.ipynb): nb-review's rendered version in the following comment.tests/test_server_compat.py) to understand how version parsing and compatiblity check logic is working for different cases