From ae375ad802b9d61e9f6f0c5baf0e54bc375eb306 Mon Sep 17 00:00:00 2001 From: Salakar Date: Wed, 3 May 2023 23:17:25 +0100 Subject: [PATCH] feat: finalize param inputs --- samples/basic_params/.firebaserc | 5 + samples/basic_params/.gitignore | 66 +++++++++ samples/basic_params/__init__.py | 3 + samples/basic_params/firebase.json | 11 ++ samples/basic_params/functions/.gitignore | 13 ++ samples/basic_params/functions/main.py | 76 +++++++++++ .../basic_params/functions/requirements.txt | 8 ++ src/firebase_functions/options.py | 19 ++- src/firebase_functions/params.py | 125 +++++++++++++----- src/firebase_functions/private/manifest.py | 52 +++++++- tests/test_manifest.py | 2 +- tests/test_options.py | 4 +- tests/test_params.py | 75 +++++------ 13 files changed, 367 insertions(+), 92 deletions(-) create mode 100644 samples/basic_params/.firebaserc create mode 100644 samples/basic_params/.gitignore create mode 100644 samples/basic_params/__init__.py create mode 100644 samples/basic_params/firebase.json create mode 100644 samples/basic_params/functions/.gitignore create mode 100644 samples/basic_params/functions/main.py create mode 100644 samples/basic_params/functions/requirements.txt diff --git a/samples/basic_params/.firebaserc b/samples/basic_params/.firebaserc new file mode 100644 index 0000000..ad27d4b --- /dev/null +++ b/samples/basic_params/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "python-functions-testing" + } +} diff --git a/samples/basic_params/.gitignore b/samples/basic_params/.gitignore new file mode 100644 index 0000000..dbb58ff --- /dev/null +++ b/samples/basic_params/.gitignore @@ -0,0 +1,66 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +firebase-debug.log* +firebase-debug.*.log* + +# Firebase cache +.firebase/ + +# Firebase config + +# Uncomment this if you'd like others to create their own Firebase project. +# For a team working on the same Firebase project(s), it is recommended to leave +# it commented so all members can deploy to the same project(s) in .firebaserc. +# .firebaserc + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env diff --git a/samples/basic_params/__init__.py b/samples/basic_params/__init__.py new file mode 100644 index 0000000..2340b04 --- /dev/null +++ b/samples/basic_params/__init__.py @@ -0,0 +1,3 @@ +# Required to avoid a 'duplicate modules' mypy error +# in monorepos that have multiple main.py files. +# https://github.com/python/mypy/issues/4008 diff --git a/samples/basic_params/firebase.json b/samples/basic_params/firebase.json new file mode 100644 index 0000000..7bbd899 --- /dev/null +++ b/samples/basic_params/firebase.json @@ -0,0 +1,11 @@ +{ + "functions": [ + { + "source": "functions", + "codebase": "default", + "ignore": [ + "venv" + ] + } + ] +} diff --git a/samples/basic_params/functions/.gitignore b/samples/basic_params/functions/.gitignore new file mode 100644 index 0000000..34cef6b --- /dev/null +++ b/samples/basic_params/functions/.gitignore @@ -0,0 +1,13 @@ +# pyenv +.python-version + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Environments +.env +.venv +venv/ +venv.bak/ +__pycache__ diff --git a/samples/basic_params/functions/main.py b/samples/basic_params/functions/main.py new file mode 100644 index 0000000..731e77b --- /dev/null +++ b/samples/basic_params/functions/main.py @@ -0,0 +1,76 @@ +""" +Example Function params & inputs. +""" +from firebase_functions import storage_fn, params +from firebase_admin import initialize_app + +initialize_app() + +bucket = params.StringParam( + "BUCKET", + label="storage bucket", + description="The bucket to resize images from.", + input=params.ResourceInput(type=params.ResourceType.STORAGE_BUCKET), + default=params.STORAGE_BUCKET, +) + +output_path = params.StringParam( + "OUTPUT_PATH", + label="storage bucket output path", + description= + "The path of in the bucket where processed images will be stored.", + input=params.TextInput( + example="/images/processed", + validation_regex=r"^\/.*$", + validation_error_message= + "Must be a valid path starting with a forward slash", + ), + default="/images/processed", +) + +image_type = params.ListParam( + "IMAGE_TYPE", + label="convert image to preferred types", + description="The image types you'd like your source image to convert to.", + input=params.MultiSelectInput([ + params.SelectOption(value="jpeg", label="jpeg"), + params.SelectOption(value="png", label="png"), + params.SelectOption(value="webp", label="webp"), + ]), + default=["jpeg", "png"], +) + +delete_original = params.BoolParam( + "DELETE_ORIGINAL_FILE", + label="delete the original file", + description= + "Do you want to automatically delete the original file from the Cloud Storage?", + input=params.SelectInput([ + params.SelectOption(value=True, label="Delete on any resize attempt"), + params.SelectOption(value=False, label="Don't delete"), + ],), + default=True, +) + +image_resize_api_secret = params.SecretParam( + "IMAGE_RESIZE_API_SECRET", + label="image resize api secret", + description="The fake secret key to use for the image resize API.", +) + + +@storage_fn.on_object_finalized( + bucket=bucket, + secrets=[image_resize_api_secret], +) +def resize_images(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]): + """ + This function will be triggered when a new object is created in the bucket. + """ + print("Got a new image:", event) + print("Selected image types:", image_type.value) + print("Selected bucket resource:", bucket.value) + print("Selected output location:", output_path.value) + print("Testing a not so secret api key:", image_resize_api_secret.value) + print("Should original images be deleted?:", delete_original.value) + # TODO: Implement your image resize logic diff --git a/samples/basic_params/functions/requirements.txt b/samples/basic_params/functions/requirements.txt new file mode 100644 index 0000000..8977a41 --- /dev/null +++ b/samples/basic_params/functions/requirements.txt @@ -0,0 +1,8 @@ +# Not published yet, +# firebase-functions-python >= 0.0.1 +# so we use a relative path during development: +./../../../ +# Or switch to git ref for deployment testing: +# git+https://github.com/firebase/firebase-functions-python.git@main#egg=firebase-functions + +firebase-admin >= 6.0.1 diff --git a/src/firebase_functions/options.py b/src/firebase_functions/options.py index d99be1f..f709ca2 100644 --- a/src/firebase_functions/options.py +++ b/src/firebase_functions/options.py @@ -313,6 +313,17 @@ def _asdict_with_global_options(self) -> dict: for option in resettable_options: if option not in merged_options: merged_options[option] = RESET_VALUE + + if self.secrets and not self.secrets == _util.Sentinel: + + def convert_secret(secret) -> str: + secret_value = secret + if isinstance(secret, SecretParam): + secret_value = secret.name + return secret_value + + merged_options["secrets"] = list( + map(convert_secret, _typing.cast(list, self.secrets))) # _util.Sentinel values are converted to `None` in ManifestEndpoint generation # after other None values are removed - so as to keep them in the generated # YAML output as 'null' values. @@ -322,7 +333,6 @@ def _endpoint(self, **kwargs) -> _manifest.ManifestEndpoint: assert kwargs["func_name"] is not None options_dict = self._asdict_with_global_options() options = self.__class__(**options_dict) - secret_envs: list[ _manifest.SecretEnvironmentVariable] | _util.Sentinel = [] if options.secrets is not None: @@ -330,10 +340,7 @@ def _endpoint(self, **kwargs) -> _manifest.ManifestEndpoint: def convert_secret( secret) -> _manifest.SecretEnvironmentVariable: - secret_value = secret - if isinstance(secret, SecretParam): - secret_value = secret.name - return {"key": secret_value} + return {"key": secret} secret_envs = list( map(convert_secret, _typing.cast(list, options.secrets))) @@ -834,7 +841,7 @@ class StorageOptions(RuntimeOptions): Internal use only. """ - bucket: str | None = None + bucket: str | Expression[str] | None = None """ The name of the bucket to watch for Storage events. """ diff --git a/src/firebase_functions/params.py b/src/firebase_functions/params.py index 08e40a1..2ff1af7 100644 --- a/src/firebase_functions/params.py +++ b/src/firebase_functions/params.py @@ -14,9 +14,11 @@ """Module for params that can make Cloud Functions codebases generic.""" import abc as _abc +import json as _json import dataclasses as _dataclasses import os as _os import re as _re +import enum as _enum import typing as _typing _T = _typing.TypeVar("_T", str, int, float, bool, list) @@ -30,21 +32,28 @@ class Expression(_abc.ABC, _typing.Generic[_T]): an Expression as the value of an option that normally accepts numbers. """ + def __cel__(self, expression: str): + object.__setattr__(self, "_cel_", expression) + + def __str__(self): + return f"{{{{ {self._cel_} }}}}" + + @property def value(self) -> _T: """ Returns the Expression's runtime value, based on the CLI's resolution of params. """ raise NotImplementedError() - def to_cel(self) -> str: - """ - Returns the Expression's representation as a braced CEL expression. - """ - return f"{{{{ {self} }}}}" + +def _obj_cel_name(obj: _T) -> _T: + return obj if not isinstance(obj, Expression) else object.__getattribute__( + obj, "_cel_") def _quote_if_string(literal: _T) -> _T: - return literal if not isinstance(literal, str) else f'"{literal}"' + return _obj_cel_name(literal) if not isinstance(literal, + str) else f'"{literal}"' _params: dict[str, Expression] = {} @@ -52,15 +61,24 @@ def _quote_if_string(literal: _T) -> _T: @_dataclasses.dataclass(frozen=True) class TernaryExpression(Expression[_T], _typing.Generic[_T]): + """ + A CEL expression that evaluates to one of two values based on the value of + another expression. + """ test: Expression[bool] if_true: _T if_false: _T - def value(self) -> _T: - return self.if_true if self.test.value() else self.if_false + def __post_init__(self): + test_str = _obj_cel_name(self.test) + true_str = _quote_if_string(self.if_true) + false_str = _quote_if_string(self.if_false) + expression = f"{test_str} ? {true_str} : {false_str}" + super().__cel__(expression) - def __str__(self) -> str: - return f"{self.test} ? {_quote_if_string(self.if_true)} : {_quote_if_string(self.if_false)}" + @property + def value(self) -> _T: + return self.if_true if self.test.value else self.if_false @_dataclasses.dataclass(frozen=True) @@ -73,8 +91,14 @@ class CompareExpression(Expression[bool], _typing.Generic[_T]): left: Expression[_T] right: _T + def __post_init__(self): + super().__cel__( + f"{_obj_cel_name(self.left)} {self.comparator} {_quote_if_string(self.right)}" + ) + + @property def value(self) -> bool: - left: _T = self.left.value() + left: _T = self.left.value if self.comparator == "==": return left == self.right elif self.comparator == ">": @@ -88,15 +112,12 @@ def value(self) -> bool: else: raise ValueError(f"Unknown comparator {self.comparator}") - def __str__(self) -> str: - return f"{self.left} {self.comparator} {_quote_if_string(self.right)}" - def then(self, if_true: _T, if_false: _T) -> TernaryExpression[_T]: return TernaryExpression(self, if_true, if_false) @_dataclasses.dataclass(frozen=True) -class SelectOptions(_typing.Generic[_T]): +class SelectOption(_typing.Generic[_T]): """ A representation of an option that can be selected via a SelectInput. """ @@ -115,7 +136,19 @@ class SelectInput(_typing.Generic[_T]): from a list of pre-canned options interactively at deploy-time. """ - options: list[SelectOptions[_T]] + options: list[SelectOption[_T]] + """A list of user selectable options.""" + + +@_dataclasses.dataclass(frozen=True) +class MultiSelectInput(): + """ + Specifies that a Param's value should be determined by having the user select + a subset from a list of pre-canned options interactively at deploy-time. + Will result in errors if used on Params of type other than string[]. + """ + + options: list[SelectOption[str]] """A list of user selectable options.""" @@ -144,6 +177,11 @@ class TextInput: """ +class ResourceType(str, _enum.Enum): + """The type of resource that a picker should pick.""" + STORAGE_BUCKET = "storage.googleapis.com/Bucket" + + @_dataclasses.dataclass(frozen=True) class ResourceInput: """ @@ -152,7 +190,7 @@ class ResourceInput: type. Currently, only type:"storage.googleapis.com/Bucket" is supported. """ - type: str + type: ResourceType | str """ The resource type. Currently, only type:"storage.googleapis.com/Bucket" is supported. """ @@ -169,7 +207,7 @@ class Param(Expression[_T]): The environment variable of this parameter. Must be upper case. """ - default: _T | None = None + default: _T | Expression[_T] | None = None """ The default value to assign to this param if none provided. """ @@ -190,11 +228,13 @@ class Param(Expression[_T]): deployments. """ - input: TextInput | ResourceInput | SelectInput[_T] = TextInput() + input: TextInput | ResourceInput | SelectInput[ + _T] | MultiSelectInput | None = None """ The type of input that is required for this param, e.g. TextInput. """ + @property def value(self) -> _T: raise NotImplementedError() @@ -204,10 +244,8 @@ def compare(self, compare: str, right: _T) -> CompareExpression: def equals(self, right: _T) -> CompareExpression: return self.compare("==", right) - def __str__(self) -> str: - return f"params.{self.name}" - def __post_init__(self): + super().__cel__(f"params.{self.name}") if isinstance(self, _DefaultStringParam): return if not _re.match(r"^[A-Z0-9_]+$", self.name): @@ -248,10 +286,8 @@ class SecretParam(Expression[str]): deployments. """ - def __str__(self): - return f"params.{self.name}" - def __post_init__(self): + super().__cel__(f"params.{self.name}") if not _re.match(r"^[A-Z0-9_]+$", self.name): raise ValueError( "Parameter names must only use uppercase letters, numbers and " @@ -262,6 +298,7 @@ def __post_init__(self): ) _params[self.name] = self + @property def value(self) -> str: """Current value of this parameter.""" return _os.environ.get(self.name, "") @@ -277,12 +314,14 @@ def equals(self, right: _T) -> CompareExpression: class StringParam(Param[str]): """A parameter as a string value.""" + @property def value(self) -> str: if _os.environ.get(self.name) is not None: return _os.environ[self.name] if self.default is not None: - return self.default + return self.default.value if isinstance( + self.default, Expression) else self.default return str() @@ -291,11 +330,13 @@ def value(self) -> str: class IntParam(Param[int]): """A parameter as a int value.""" + @property def value(self) -> int: if _os.environ.get(self.name) is not None: return int(_os.environ[self.name]) if self.default is not None: - return self.default + return self.default.value if isinstance( + self.default, Expression) else self.default return int() @@ -303,11 +344,13 @@ def value(self) -> int: class FloatParam(Param[float]): """A parameter as a float value.""" + @property def value(self) -> float: if _os.environ.get(self.name) is not None: return float(_os.environ[self.name]) if self.default is not None: - return self.default + return self.default.value if isinstance( + self.default, Expression) else self.default return float() @@ -315,16 +358,14 @@ def value(self) -> float: class BoolParam(Param[bool]): """A parameter as a bool value.""" + @property def value(self) -> bool: env_value = _os.environ.get(self.name) if env_value is not None: - if env_value.lower() in ["true", "t", "1", "y", "yes"]: - return True - if env_value.lower() in ["false", "f", "0", "n", "no"]: - return False - raise ValueError(f"Invalid value for {self.name}: {env_value}") + return env_value.lower() == "true" if self.default is not None: - return self.default + return self.default.value if isinstance( + self.default, Expression) else self.default return False @@ -332,11 +373,25 @@ def value(self) -> bool: class ListParam(Param[list]): """A parameter as a list of strings.""" + @property def value(self) -> list[str]: if _os.environ.get(self.name) is not None: + # If the environment variable starts with "[" and ends with "]", + # then assume it is a JSON array and try to parse it. + # (This is for Cloud Run (v2 Functions), the environment variable is a JSON array.) + if _os.environ[self.name].startswith("[") and _os.environ[ + self.name].endswith("]"): + try: + return _json.loads(_os.environ[self.name]) + except _json.JSONDecodeError: + return [] + # Otherwise, split the string by commas. + # (This is for emulator & the Firebase CLI generated .env file, the environment + # variable is a comma-separated list.) return list(filter(len, _os.environ[self.name].split(","))) if self.default is not None: - return self.default + return self.default.value if isinstance( + self.default, Expression) else self.default return [] diff --git a/src/firebase_functions/private/manifest.py b/src/firebase_functions/private/manifest.py index 864cb22..15227d5 100644 --- a/src/firebase_functions/private/manifest.py +++ b/src/firebase_functions/private/manifest.py @@ -180,6 +180,48 @@ class ManifestStack: default_factory=list[ManifestRequiredApi]) +def _param_input_to_spec( + param_input: _params.TextInput | _params.ResourceInput | + _params.SelectInput | _params.MultiSelectInput +) -> dict[str, _typing.Any]: + if isinstance(param_input, _params.TextInput): + return { + "text": { + key: value for key, value in { + "example": + param_input.example, + "validationRegex": + param_input.validation_regex, + "validationErrorMessage": + param_input.validation_error_message, + }.items() if value is not None + } + } + + if isinstance(param_input, _params.ResourceInput): + return { + "resource": { + "type": param_input.type, + }, + } + + if isinstance(param_input, (_params.MultiSelectInput, _params.SelectInput)): + key = "select" if isinstance(param_input, + _params.SelectInput) else "multiSelect" + return { + key: { + "options": [{ + key: value for key, value in { + "value": option.value, + "label": option.label, + }.items() if value is not None + } for option in param_input.options], + }, + } + + return {} + + def _param_to_spec( param: _params.Param | _params.SecretParam) -> dict[str, _typing.Any]: spec_dict: dict[str, _typing.Any] = { @@ -190,8 +232,10 @@ def _param_to_spec( } if isinstance(param, _params.Param): - spec_dict["default"] = param.default - # TODO spec representation of inputs + spec_dict["default"] = f"{param.default}" if isinstance( + param.default, _params.Expression) else param.default + if param.input: + spec_dict["input"] = _param_input_to_spec(param.input) if isinstance(param, _params.BoolParam): spec_dict["type"] = "boolean" @@ -203,8 +247,6 @@ def _param_to_spec( spec_dict["type"] = "secret" elif isinstance(param, _params.ListParam): spec_dict["type"] = "list" - if spec_dict["default"] is not None: - spec_dict["default"] = ",".join(spec_dict["default"]) elif isinstance(param, _params.StringParam): spec_dict["type"] = "string" else: @@ -217,7 +259,7 @@ def _object_to_spec(data) -> object: if isinstance(data, _Enum): return data.value elif isinstance(data, _params.Expression): - return data.to_cel() + return f"{data}" elif _dataclasses.is_dataclass(data): return _dataclass_to_spec(data) elif isinstance(data, list): diff --git a/tests/test_manifest.py b/tests/test_manifest.py index 3c4390c..dc0db9c 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -99,7 +99,7 @@ "name": "STRING_TEST", "type": "string" }, { - "default": "1,2,3", + "default": ["1", "2", "3"], "name": "LIST_TEST", "type": "list" }], diff --git a/tests/test_options.py b/tests/test_options.py index 8dc0cba..4c2913a 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -72,8 +72,8 @@ def test_options_asdict_uses_cel_representation(): int_param = params.IntParam("MIN") https_options_dict = options.HttpsOptions( min_instances=int_param)._asdict_with_global_options() - assert https_options_dict["min_instances"] == int_param.to_cel( - ), "param was not converted to CEL string" + assert https_options_dict[ + "min_instances"] == f"{int_param}", "param was not converted to CEL string" def test_options_preserve_external_changes(): diff --git a/tests/test_params.py b/tests/test_params.py index 39284e8..b367bf6 100644 --- a/tests/test_params.py +++ b/tests/test_params.py @@ -24,42 +24,31 @@ class TestBoolParams: def test_bool_param_value_true_or_false(self): """Testing if bool params correctly returns a true or false value.""" bool_param = params.BoolParam("BOOL_VALUE_TEST1") - for value_true, value_false in zip(["true", "t", "1", "y", "yes"], - ["false", "f", "0", "n", "no"]): + for value_true, value_false in zip(["true"], + ["false", "anything", "else"]): environ["BOOL_VALUE_TEST1"] = value_true - assert (bool_param.value() - is True), "Failure, params returned False" + assert (bool_param.value is True), "Failure, params returned False" environ["BOOL_VALUE_TEST1"] = value_false - assert (bool_param.value() - is False), "Failure, params returned True" - - def test_bool_param_value_error(self): - """Testing if bool params throws a value error if invalid value.""" - with pytest.raises(ValueError): - environ["BOOL_VALUE_TEST2"] = "bad_value" - params.BoolParam("BOOL_VALUE_TEST2").value() + assert (bool_param.value is False), "Failure, params returned True" def test_bool_param_empty_default(self): """Testing if bool params defaults to False if no value and no default.""" - assert (params.BoolParam("BOOL_DEFAULT_TEST").value() + assert (params.BoolParam("BOOL_DEFAULT_TEST").value is False), "Failure, params returned True" def test_bool_param_default(self): """Testing if bool params defaults to provided default value.""" - assert (params.BoolParam("BOOL_DEFAULT_TEST_FALSE", - default=False).value() + assert (params.BoolParam("BOOL_DEFAULT_TEST_FALSE", default=False).value is False), "Failure, params returned True" - assert (params.BoolParam("BOOL_DEFAULT_TEST_TRUE", - default=True).value() + assert (params.BoolParam("BOOL_DEFAULT_TEST_TRUE", default=True).value is True), "Failure, params returned False" def test_bool_param_equality(self): """Test bool equality.""" assert (params.BoolParam("BOOL_TEST1", - default=False).equals(False).value() + default=False).equals(False).value is True), "Failure, equality check returned False" - assert (params.BoolParam("BOOL_TEST2", - default=True).equals(False).value() + assert (params.BoolParam("BOOL_TEST2", default=True).equals(False).value is False), "Failure, equality check returned False" @@ -69,27 +58,27 @@ class TestFloatParams: def test_float_param_value(self): """Testing if float params correctly returns a value.""" environ["FLOAT_VALUE_TEST"] = "123.456" - assert params.FloatParam("FLOAT_VALUE_TEST",).value() == 123.456, \ + assert params.FloatParam("FLOAT_VALUE_TEST",).value == 123.456, \ "Failure, params value != 123.456" def test_float_param_empty_default(self): """Testing if float params defaults to empty float if no value and no default.""" - assert params.FloatParam("FLOAT_DEFAULT_TEST1").value() == float(), \ + assert params.FloatParam("FLOAT_DEFAULT_TEST1").value == float(), \ "Failure, params value is not float" def test_float_param_default(self): """Testing if float param defaults to provided default value.""" assert params.FloatParam("FLOAT_DEFAULT_TEST2", - default=float(456.789)).value() == 456.789, \ + default=float(456.789)).value == 456.789, \ "Failure, params default value != 456.789" def test_float_param_equality(self): """Test float equality.""" assert (params.FloatParam("FLOAT_TEST1", - default=123.456).equals(123.456).value() + default=123.456).equals(123.456).value is True), "Failure, equality check returned False" assert (params.FloatParam("FLOAT_TEST2", - default=456.789).equals(123.456).value() + default=456.789).equals(123.456).value is False), "Failure, equality check returned False" @@ -100,23 +89,23 @@ def test_int_param_value(self): """Testing if int param correctly returns a value.""" environ["INT_VALUE_TEST"] = "123" assert params.IntParam( - "INT_VALUE_TEST").value() == 123, "Failure, params value != 123" + "INT_VALUE_TEST").value == 123, "Failure, params value != 123" def test_int_param_empty_default(self): """Testing if int param defaults to empty int if no value and no default.""" - assert params.IntParam("INT_DEFAULT_TEST1").value() == int( + assert params.IntParam("INT_DEFAULT_TEST1").value == int( ), "Failure, params value is not int" def test_int_param_default(self): """Testing if int param defaults to provided default value.""" - assert params.IntParam("INT_DEFAULT_TEST2", default=456).value() == 456, \ + assert params.IntParam("INT_DEFAULT_TEST2", default=456).value == 456, \ "Failure, params default value != 456" def test_int_param_equality(self): """Test int equality.""" - assert (params.IntParam("INT_TEST1", default=123).equals(123).value() + assert (params.IntParam("INT_TEST1", default=123).equals(123).value is True), "Failure, equality check returned False" - assert (params.IntParam("INT_TEST2", default=456).equals(123).value() + assert (params.IntParam("INT_TEST2", default=456).equals(123).value is False), "Failure, equality check returned False" @@ -126,7 +115,7 @@ class TestStringParams: def test_string_param_value(self): """Testing if string param correctly returns a value.""" environ["STRING_VALUE_TEST"] = "STRING_TEST" - assert params.StringParam("STRING_VALUE_TEST").value() == "STRING_TEST", \ + assert params.StringParam("STRING_VALUE_TEST").value == "STRING_TEST", \ 'Failure, params value != "STRING_TEST"' def test_param_name_upper_snake_case(self): @@ -137,23 +126,23 @@ def test_param_name_upper_snake_case(self): def test_string_param_empty_default(self): """Testing if string param defaults to empty string if no value and no default.""" - assert params.StringParam("STRING_DEFAULT_TEST1").value() == str(), \ + assert params.StringParam("STRING_DEFAULT_TEST1").value == str(), \ "Failure, params value is not a string" def test_string_param_default(self): """Testing if string param defaults to provided default value.""" assert (params.StringParam("STRING_DEFAULT_TEST2", - default="string_override_default").value() + default="string_override_default").value == "string_override_default"), \ 'Failure, params default value != "string_override_default"' def test_string_param_equality(self): """Test string equality.""" assert (params.StringParam("STRING_TEST1", - default="123").equals("123").value() + default="123").equals("123").value is True), "Failure, equality check returned False" assert (params.StringParam("STRING_TEST2", - default="456").equals("123").value() + default="456").equals("123").value is False), "Failure, equality check returned False" @@ -163,33 +152,33 @@ class TestListParams: def test_list_param_value(self): """Testing if list param correctly returns list values.""" environ["LIST_VALUE_TEST1"] = "item1,item2" - assert params.ListParam("LIST_VALUE_TEST1").value() == ["item1","item2"], \ + assert params.ListParam("LIST_VALUE_TEST1").value == ["item1","item2"], \ 'Failure, params value != ["item1","item2"]' def test_list_param_filter_empty_strings(self): """Testing if list param correctly returns list values wth empty strings excluded.""" environ["LIST_VALUE_TEST2"] = ",,item1,item2,,,item3," - assert params.ListParam("LIST_VALUE_TEST2").value() == ["item1","item2", "item3"], \ + assert params.ListParam("LIST_VALUE_TEST2").value == ["item1","item2", "item3"], \ 'Failure, params value != ["item1","item2", "item3"]' def test_list_param_empty_default(self): """Testing if list param defaults to an empty list if no value and no default.""" - assert params.ListParam("LIST_DEFAULT_TEST1").value() == [], \ + assert params.ListParam("LIST_DEFAULT_TEST1").value == [], \ "Failure, params value is not an empty list" def test_list_param_default(self): """Testing if list param defaults to the provided default value.""" - assert (params.ListParam("LIST_DEFAULT_TEST2", default=["1", "2"]).value() + assert (params.ListParam("LIST_DEFAULT_TEST2", default=["1", "2"]).value == ["1", "2"]), \ 'Failure, params default value != ["1", "2"]' def test_list_param_equality(self): """Test list equality.""" assert (params.ListParam("LIST_TEST1", - default=["123"]).equals(["123"]).value() + default=["123"]).equals(["123"]).value is True), "Failure, equality check returned False" assert (params.ListParam("LIST_TEST2", - default=["456"]).equals(["123"]).value() + default=["456"]).equals(["123"]).value is False), "Failure, equality check returned False" @@ -203,7 +192,7 @@ def test_params_stored(self): """Testing if params are internally stored.""" environ["TEST_STORING"] = "TEST_STORING_VALUE" param = params.StringParam("TEST_STORING") - assert param.value() == "TEST_STORING_VALUE", \ + assert param.value == "TEST_STORING_VALUE", \ 'Failure, params value != "TEST_STORING_VALUE"' # pylint: disable=protected-access assert params._params["TEST_STORING"] == param, \ @@ -213,7 +202,7 @@ def test_default_params_not_stored(self): """Testing if default params are skipped from being stored.""" environ["GCLOUD_PROJECT"] = "python-testing-project" - assert params.PROJECT_ID.value() == "python-testing-project", \ + assert params.PROJECT_ID.value == "python-testing-project", \ 'Failure, params value != "python-testing-project"' # pylint: disable=protected-access assert params._params.get("GCLOUD_PROJECT") is None, \