-
Notifications
You must be signed in to change notification settings - Fork 12
Updating tests and modifications of errors to standardize their messages #30
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
Closed
hippolyteblot
wants to merge
37
commits into
virtual-imaging-platform:develop
from
hippolyteblot:develop
Closed
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
ad9006b
Create main.yml
hippolyteblot 6a8917c
Commit to launch actions
hippolyteblot 0180316
Update main.yml
hippolyteblot 3da3ee6
Update main.yml
hippolyteblot 9c67b82
Update main.yml
hippolyteblot 9294eae
Update main.yml
hippolyteblot 927d802
Update main.yml
hippolyteblot bf4ccfc
Update main.yml
hippolyteblot c2062f9
Update main.yml
hippolyteblot abeb7d9
Update main.yml
hippolyteblot 0ac9f67
Commit to launch actions
hippolyteblot bd34043
Merge branch 'develop' of github.com:hippolyteblot/VIP-python-client …
hippolyteblot 35c6c79
Commit the tmp file to test if it works
hippolyteblot cb2a85e
New patch for unlink function (linux)
hippolyteblot 41c08cc
New patch for unlink function (linux)
hippolyteblot d812f99
First tests for VipCI
hippolyteblot 1c28c60
Add girder client as dependency with pip
hippolyteblot 8180a0b
First tests for VipSession
hippolyteblot ce1612c
Refactoring tests for VipLauncher
hippolyteblot 32cb494
Refactoring tests for VipCI
hippolyteblot 761771d
Refactoring for all the tests
hippolyteblot 58aeb78
Trying to remove warning
hippolyteblot 3f970ed
Update main.yml
hippolyteblot 541a7f9
Update main.yml
hippolyteblot c6ae2e6
Refactoring to avoid warning and standardize error output
hippolyteblot 6634451
Merge branch 'develop' of github.com:hippolyteblot/VIP-python-client …
hippolyteblot 5221d2c
Update main.yml
hippolyteblot c39cd19
Tests for vip utils
hippolyteblot a80e1e2
Merge branch 'develop' of github.com:hippolyteblot/VIP-python-client …
hippolyteblot 046fbb8
Fix error division by 0
hippolyteblot 3309376
Removing print
hippolyteblot 30ddfe1
Adding a delay for creating the tests dir
hippolyteblot 74feba1
Fix vip utils tests
hippolyteblot 73c3942
Fix tests and support for executions with only default values
hippolyteblot 24f25fd
fix
hippolyteblot 3c4ce50
Merge remote-tracking branch 'upstream/develop' into develop
hippolyteblot 2c978c5
fix
hippolyteblot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| - develop | ||
| pull_request: | ||
| branches: | ||
| - develop | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| env: | ||
| VIP_API_KEY: ${{ secrets.VIP_API_KEY }} | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python 3.9 | ||
| uses: actions/setup-python@v4 | ||
| with: | ||
| python-version: 3.9 | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install pytest pytest_mock requests girder_client | ||
|
|
||
| - name: Run Pytest | ||
| run: | | ||
| export PYTHONPATH=src:$PYTHONPATH | ||
| pytest |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,11 @@ | ||
| __pycache__/ | ||
| .pytest_cache/ | ||
| .vscode/ | ||
| vip_outputs/ | ||
| perso/ | ||
|
|
||
| examples/old | ||
|
|
||
| publish.sh | ||
| dist/ | ||
| tests/old | ||
| tests/__pycache__ | ||
| .env | ||
| test_pipedef.py |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,14 +146,10 @@ def input_settings(self, input_settings: dict): | |
| # Check type | ||
| if not isinstance(input_settings, dict): | ||
| raise TypeError("`input_settings` should be a dictionary") | ||
| # Check if each input can be converted to a string with valid characters for VIP | ||
| for param, value in input_settings.items(): | ||
| invalid = self._invalid_chars_for_vip(value) | ||
| # if not (set(invalid) <= {'\\'}): # '\\' is OK for Windows paths #EDIT: Corrected in _invalid_chars_for_vip | ||
| if invalid: | ||
| raise ValueError( | ||
| f"Parameter '{param}' contains some invalid character(s): {', '.join(invalid)}" | ||
| ) | ||
|
|
||
| # Check if each input can be converted to a string with valid characters and no empty strings | ||
| self._get_invalid_input(input_settings) | ||
|
|
||
| # Parse the input settings | ||
| new_settings = self._parse_input_settings(input_settings) | ||
| self._print("parsed") | ||
|
|
@@ -489,10 +485,7 @@ def launch_pipeline( | |
| self._print("OK") | ||
| # Check the input parameters | ||
| self._print("Input settings: ", end="", flush=True) | ||
| # Check existence | ||
| if not self._is_defined("_input_settings"): | ||
| raise TypeError("Please provide input parameters for Session: %s" %self._session_name) | ||
| # Check content | ||
| # Check content | ||
| self._check_input_settings(location=self._SERVER_NAME) | ||
| self._print("OK") | ||
| # End parameters checks | ||
|
|
@@ -602,7 +595,7 @@ def run_session( self, nb_runs=1, refresh_time=30) -> VipLauncher: | |
| 1. Launches pipeline executions on VIP; | ||
| 2. Monitors pipeline executions until they are all over. | ||
|
|
||
| /!\ This function assumes that all session properties are already set. | ||
| |!| This function assumes that all session properties are already set. | ||
| Optional arguments can be provided: | ||
| - Increase `nb_runs` to run more than 1 execution at once; | ||
| - Set `refresh_time` to modify the default refresh time; | ||
|
|
@@ -1562,7 +1555,7 @@ def _check_input_settings(self, input_settings: dict=None, location: str=None) - | |
| if self._is_defined("_input_settings"): | ||
| input_settings = self._get_input_settings(location) | ||
| else: | ||
| raise AttributeError("Input settings are missing") | ||
| input_settings = {} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So input_setting is optional now ? |
||
| # Check the pipeline identifier | ||
| if not self._is_defined("_pipeline_id"): | ||
| raise AttributeError("Input settings could not be checked without a pipeline identifier.") | ||
|
|
@@ -1584,14 +1577,14 @@ def _check_input_keys(self, input_settings: dict) -> None: | |
| # required parameters without a default value | ||
| { | ||
| param["name"] for param in self._pipeline_def['parameters'] | ||
| if not param["isOptional"] and (param["defaultValue"] == '$input.getDefaultValue()') | ||
| if not param["isOptional"] and param["defaultValue"] == None | ||
| } | ||
| # current parameters | ||
| - set(input_settings.keys()) | ||
| ) | ||
| # Raise an error if a field is missing | ||
| if missing_fields: | ||
| raise TypeError("Missing input parameter(s) :\n" + ", ".join(missing_fields)) | ||
| raise AttributeError("Missing input parameter(s): " + ", ".join(sorted(missing_fields))) | ||
| # Check every input parameter is a valid field | ||
| unknown_fields = ( | ||
| set(input_settings.keys()) # current parameters | ||
|
|
@@ -1603,50 +1596,74 @@ def _check_input_keys(self, input_settings: dict) -> None: | |
| + f"'] are useless for pipeline '{self._pipeline_id}'. This may throw RuntimeError later.") | ||
| # ------------------------------------------------ | ||
|
|
||
| # Check the parameter values according to pipeline descriptor | ||
| def _check_input_values(self, input_settings: dict, location: str) -> None: | ||
| """ | ||
| Checks if each parameter value in `input_settings` matches its pipeline description in `parameters`. | ||
| `location` refers to the storage infrastructure (e.g., VIP) to scan for missing files. | ||
|
|
||
| Prerequisite: input_settings is defined and contains only strings, or lists of strings. | ||
| """ | ||
| # Browse the input parameters | ||
| for param in self._pipeline_def['parameters'] : | ||
|
|
||
| def _get_invalid_input(self, input_settings: dict, parameters_ref=None): | ||
| missing_inputs = [] | ||
| invalid_chars_inputs = [] | ||
| if not parameters_ref: # Check from itself if no parameters_ref provided | ||
| parameters_ref = [{"name": param} for param in input_settings.keys()] | ||
| for param in parameters_ref: | ||
| # Get parameter name | ||
| name = param['name'] | ||
| # Skip irrelevant inputs (this should not happen after self._check_input_keys()) | ||
| if name not in input_settings: | ||
| continue | ||
| # Get input value | ||
| value = input_settings[name] | ||
| # `request` will send only strings | ||
| if not self._isinstance(value, str): # This should not happen | ||
| raise ValueError( # Parameter could not be parsed correctly | ||
| f"Parameter: '{name}' \n\twith value: '{value}' \n\twith type: '{type(value)}')\ncould not be parsed."\ | ||
| +"Please double check the value; if correct, try converting it to `str` in the `input_settings`." | ||
| ) | ||
|
|
||
| # Check the input has no empty values | ||
| if not self._is_input_full(value): | ||
| raise ValueError( | ||
| f"Parameter '{name}' contains an empty value" | ||
| ) | ||
| missing_inputs.append(name) | ||
hippolyteblot marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| continue | ||
| # Check invalid characters for VIP | ||
| invalid = self._invalid_chars_for_vip(value) | ||
| if invalid: | ||
| raise ValueError( | ||
| f"Parameter '{name}' contains some invalid character(s): {', '.join(invalid)}" | ||
| ) | ||
| # If input is a File, check file(s) existence | ||
| invalid_chars_inputs.append((name, invalid)) | ||
| continue | ||
|
|
||
| if missing_inputs: | ||
| raise ValueError( | ||
| f"Missing input value(s) for parameter(s): {', '.join(sorted(missing_inputs))}" | ||
| ) | ||
| if invalid_chars_inputs: | ||
| raise ValueError( | ||
| f"Invalid character(s) in input value(s) for parameter(s): {', '.join(sorted(invalid_chars_inputs))}" | ||
| ) | ||
|
|
||
| # Check the parameter values according to pipeline descriptor | ||
| def _check_input_values(self, input_settings: dict, location: str) -> None: | ||
| """ | ||
| Checks if each parameter value in `input_settings` matches its pipeline description in `parameters`. | ||
| `location` refers to the storage infrastructure (e.g., VIP) to scan for missing files. | ||
|
|
||
| Prerequisite: input_settings is defined and contains only strings, or lists of strings. | ||
| """ | ||
| self._get_invalid_input(input_settings, self._pipeline_def['parameters']) | ||
|
|
||
| wrong_type_inputs = [] | ||
| for param in self._pipeline_def['parameters']: | ||
| name = param['name'] | ||
| value = input_settings.get(name) | ||
| # If input is a File, check file(s) existence | ||
| if param["type"] == "File": | ||
| # Ensure every file exists at `location` | ||
| missing_file = self._first_missing_file(value, location) | ||
| if missing_file: | ||
| raise FileNotFoundError( | ||
| f"Parameter '{name}': The following file is missing in the {location.upper()} file system:\n\t{missing_file}" | ||
| f"Parameter '{name}': The following file is missing in the {location.upper()} file system: {missing_file}" | ||
| ) | ||
| if param["type"] == "Boolean": | ||
| if value not in ["true", "false"]: | ||
| wrong_type_inputs.append(name) | ||
| continue | ||
| # Check other input formats ? | ||
| else: pass # TODO | ||
|
|
||
| if wrong_type_inputs: | ||
| raise ValueError( | ||
| f"Wrong type(s) for parameter(s): {', '.join(sorted(wrong_type_inputs))}" | ||
| ) | ||
|
|
||
| # ------------------------------------------------ | ||
|
|
||
| # Function to look for empty values | ||
|
|
@@ -1655,12 +1672,10 @@ def _is_input_full(cls, value): | |
| """ | ||
| Returns False if `value` contains an empty string or list. | ||
| """ | ||
| if isinstance(value, list) and cls._isinstance(value, str): # Case: list of strings | ||
| return all([(len(v) > 0) for v in value]) | ||
| elif isinstance(value, (str, list)): # Case: list or string | ||
| return (len(value) > 0) | ||
| else: # Case: other | ||
| return True | ||
| if isinstance(value, list): # Case: list | ||
| return len(value) > 0 and all([cls._is_input_full(v) for v in value]) | ||
| else: | ||
| return (len(str(value)) > 0) | ||
| # ------------------------------------------------ | ||
|
|
||
| # Function to assert the input contains only a certain Python type | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -542,7 +542,7 @@ def run_session( | |
| 3. Monitors pipeline executions until they are all over; | ||
| 4. Downloads execution results from VIP. | ||
|
|
||
| /!\ This method assumes that all session properties are already set. | ||
| |!| This method assumes that all session properties are already set. | ||
| Optional arguments can still be provided: | ||
| - Set `update_files` to False to avoid checking the input data on VIP; | ||
| - Increase `nb_runs` to run more than 1 execution at once; | ||
|
|
@@ -1075,8 +1075,8 @@ def parse_value(input): | |
| # Case: single input, string or path-like | ||
| elif isinstance(input, (str, os.PathLike)): | ||
| # Case: VIP path | ||
| if str(input).startswith(self._SERVER_PATH_PREFIX): # PurePath.is_relative_to() is unavailable for Python <3.9 | ||
| if self._is_defined('_vip_input_dir'): | ||
| if str(input).startswith(self._SERVER_PATH_PREFIX) and len(str(input)) > 0: # PurePath.is_relative_to() is unavailable for Python <3.9 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i guesse input can be empy at this point ? it is not verified yet ? |
||
| if self._is_defined('_vip_input_dir'): | ||
| input_dir = self._vip_input_dir | ||
| input_path = PurePosixPath(input) | ||
| else: # Return input if `_vip_input_dir` is unset | ||
|
|
@@ -1091,8 +1091,10 @@ def parse_value(input): | |
| return input | ||
| # Return the part of `input_path` that is relative to `input_dir` (if relevant) | ||
| try: # No condition since PurePath.is_relative_to() is unavailable for Python <3.9 | ||
| return PurePosixPath( # Force Posix flavor to avoid conflicts with Windows paths when checking equality | ||
| input_path.relative_to(input_dir)) # Relative part of `input_path` | ||
| if len(str(input)) > 0: | ||
| return PurePosixPath( # Force Posix flavor to avoid conflicts with Windows paths when checking equality | ||
| input_path.relative_to(input_dir)) # Relative part of `input_path` | ||
| return input | ||
| except ValueError: | ||
| # This is the case when no relative part could be found | ||
| return input | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
|
|
||
| class FakeGirderClient(): | ||
|
|
||
| pipeline_id = "LCModel/0.1" | ||
| def __init__(self, apiUrl): | ||
| pass | ||
| def authenticate(self, apiKey): | ||
| return True | ||
|
|
||
| def resourceLookup(self, path): | ||
| return {'_id': 'fake_id', '_modelType': 'folder'} | ||
|
|
||
| def createFolder(self, parentId, name, reuseExisting=True, **kwargs): | ||
| return {'_id': 'fake_id'} | ||
|
|
||
| def addMetadataToFolder(self, folderId, metadata): | ||
| return True | ||
|
|
||
| def getFolder(cls, folderId): | ||
| metadata = { | ||
| 'input_settings': { | ||
| 'zipped_folder': 'fake_value', | ||
| 'basis_file': 'fake_value', | ||
| 'signal_file': ['fake_value', 'fake_value'], | ||
| 'control_file': ['fake_value']}, | ||
| "pipeline_id": cls.pipeline_id, | ||
| 'session_name': 'test-VipLauncher', | ||
| 'workflows': {}, | ||
| "vip_output_dir": "/vip/Home/test-VipLauncher/OUTPUTS" | ||
| } | ||
| return {'_id': 'fake_id', 'meta': metadata} | ||
|
|
||
| def get(self, path): | ||
| return {'_id': 'fake_id'} | ||
|
|
||
| def listFiles(self, folderId): | ||
| return [{'_id': 'fake_id'}] | ||
|
|
||
| def listItem(self, folderId): | ||
| return {'_id': 'fake_id'} | ||
|
|
||
| @classmethod | ||
| def set_pipeline_id(cls, pipeline_id): | ||
| cls.pipeline_id = pipeline_id |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| This is juste a test file for upload |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| This is juste a test file for upload |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
It is removed because it is verified in
_check_input_settings?