Source code for pylero.base_polarion

# -*- coding: utf8 -*-
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
from pylero._compatible import SafeConfigParser, basestring, classmethod
import os
import base64
import copy
import re
import suds
from pylero.exceptions import PyleroLibException
from pylero.server import Server
from functools import wraps
from getpass import getpass

# classproperty is a property that works on the class level

[docs]class ClassProperty(property): """Returns a classmethod as the getter so that the property can be used as a class property. This is needed so that the property can be set for all child objects. This project currently has no need of a setter or deleter. """ def __get__(self, instance, cls): return classmethod(self.fget).__get__(instance, cls)()
[docs]class Configuration(object): pkgdir = os.path.dirname(__file__) GLOBAL_CONFIG = "%s/etc/pylero.cfg" % pkgdir LOCAL_CONFIG = os.path.expanduser("~") + "/.pylero" CURDIR_CONFIG = ".pylero" CONFIG_SECTION = "webservice" # Look at ConfigParser - https://docs.python.org/2.6/library/configparser.html
[docs] def __init__(self): defaults = {"cachingpolicy": "0", "timeout": "120"} config = SafeConfigParser(defaults) # Check for existence of config file and config_section if not config.read([self.GLOBAL_CONFIG, self.LOCAL_CONFIG, self.CURDIR_CONFIG]) or \ not config.has_section(self.CONFIG_SECTION): # Check for mandatory environ variables if config file is not found if not all(os.environ.get(item) for item in ('POLARION_URL', 'POLARION_REPO', 'POLARION_USERNAME', 'POLARION_PASSWORD', 'POLARION_PROJECT')): raise PyleroLibException("The config files/ENV vars do not " "exist or are not of the correct " "format. Valid files are: {0}, {1} " "or {2}" .format(self.GLOBAL_CONFIG, self.LOCAL_CONFIG, self.CURDIR_CONFIG)) self.server_url = os.environ.get("POLARION_URL") or \ config.get(self.CONFIG_SECTION, "url") self.repo = os.environ.get("POLARION_REPO") or \ config.get(self.CONFIG_SECTION, "svn_repo") self.login = os.environ.get("POLARION_USERNAME") or \ config.get(self.CONFIG_SECTION, "user") self.pwd = os.environ.get("POLARION_PASSWORD") or \ config.get(self.CONFIG_SECTION, "password") try: self.timeout = os.environ.get("POLARION_TIMEOUT") or \ config.get(self.CONFIG_SECTION, "timeout") except: self.timeout = config.defaults()['timeout'] try: self.timeout = int(self.timeout) except ValueError: raise PyleroLibException("The timeout value in the config" " file must be an integer") self.proj = os.environ.get("POLARION_PROJECT") or \ config.get(self.CONFIG_SECTION, "default_project") try: self.cert_path = os.environ.get("POLARION_CERT_PATH") or \ config.get(self.CONFIG_SECTION, "cert_path") except: self.cert_path = None if not (self.server_url and self.login and self.proj): raise PyleroLibException("The config files must contain " "valid values for: url, user, " "password and default_project")
[docs]class Connection(object): """Creates a Polarion session as a class method, so that it is used for all objects inherited by BasePolarion. The url, repo, user and password are read from config files, which are located either the current directory ./pylero, the user's dir ~/.pylero or the Library config dir LIBDIR/etc/pylero.cfg These can also be overridden with the following environment variables: POLARION_URL POLARION_REPO POLARION_USERNAME POLARION_PASSWORD POLARION_TIMEOUT POLARION_PROJECT """ connected = False session = None password_retries = 3 @classmethod def session(cls): if not cls.connected: cfg = Configuration() # if the password is not supplied in the config file, ask the user # for it if not cfg.pwd: cfg.pwd = getpass( "Password not in config file.\nEnter Password:") while not cls.connected and cls.password_retries: try: srv = Server( cfg.server_url, cfg.login, cfg.pwd, timeout=cfg.timeout, cert_path=cfg.cert_path) cls.session = srv.session() cls.session._login() cls.connected = True except suds.WebFault as e: # If we couldn't connect its because the user has typed the wrong # password. So we keep asking for password till we are successfully # connected if "com.polarion.platform.security." \ "AuthenticationFailedException" \ in e.fault.faultstring: pwd = getpass("Invalid Password.\nEnter Password:") cls.password_retries -= 1 else: raise if not cls.password_retries: raise PyleroLibException("Unable to establish pylero session " "due to 3 incorrect login attempts") cls.session.default_project = cfg.proj cls.session.user_id = cfg.login cls.session.password = cfg.pwd cls.session.repo = cfg.repo # must use try/except instead of or because the config file # may return a non empty value, such as " " return cls.session
[docs]def tx_wrapper(func): # decorator function to run specific functions in. # Because they have multiple modifying stmts, they should be run # in a transaction. It first checks if it is already in a tx and if # not, it starts one itself. @wraps(func) def inner(*args, **kwargs): # the first object is the instance or class object. self = args[0] new_tx = False try: if not self.session.tx_in(): self.session.tx_begin() new_tx = True res = func(*args, **kwargs) if new_tx: self.session.tx_commit() return res except (suds.WebFault, PyleroLibException, Exception): if new_tx and self.session.tx_in(): self.session.tx_rollback() raise return inner
[docs]class BasePolarion(object): """BasePolarion is the parent class for all the WSDL Polarion objects that are published. Using the _cls_suds_map, the class creates a property for each attribute so that any access of the object attribute, will access the WSDL object that is contained by it. Attributes: _cls_suds_map (dict): maps the Polarion attribute names to the Pylero attribute names. Attributes that reference either objects or an array of objects have the properties relate to the relationship meaning that accessing the property will give access to the object or list of objects. _id_field (str): the field that represents an id field, used in the child class's constructor. when a child's class defines the field it allows this constructor to accept an obj_id as a parameter _obj_client (str): The Polarion client child's WSDL object is defined by _obj_struct (str): The data type defined by the WSDL library. The structure of the datatype is tnsX:ObjectName, the X is per datatype and has no default. session (Session): The Polarion Session object, initialized by the Connection class. This attribute connects to the server one time per session, no matter how many objects are instantiated. default_project (str): The user's default project, to be used when project_id is needed and there is none given """ _cls_suds_map = {} _id_field = None _obj_client = None _obj_struct = None _session = None _default_project = None _cache = { "enums": {}, "custom_field_types": {}, "projects": {} } REGEX_PROJ = "/default/(.*)\$" # The id in the uri is always after the last }, at times there are multiple REGEX_ID = ".+}(.*)$" # The URI_STRUCT can be overridden in a child class when needed (for # example, Documents). Also if there is need for a replace in the child # class the URI_ID lambda attributes should be overridden URI_STRUCT = "subterra:data-service:objects:/default/" \ "%(project)s${%(obj)s}%(id)s" # must wrap lambda with classmethod so it can be used as such URI_ID_GET_REPLACE = classmethod(lambda cls, x: x) URI_ID_SET_REPLACE = classmethod(lambda cls, x: x) @ClassProperty def session(cls): # Uses a class property for the session, so that the library doesn't # connect to the server until the library is actually used. if BasePolarion._session: return BasePolarion._session else: # For some reason, using the cls attribute makes it into a class # attribute for the specific class but not for all the other # Pylero objects. BasePolarion._session = Connection.session() BasePolarion._default_project = cls._session.default_project BasePolarion.logged_in_user_id = cls._session.user_id # stores password in the session so it can be used for direct svn # operations BasePolarion.repo = cls._session.repo return BasePolarion._session @ClassProperty def default_project(cls): # Uses a class property for the session, so that the library connects # when accessing it. if not BasePolarion._default_project: cls.session return BasePolarion._default_project @classmethod def _convert_obj_fields_to_polarion(cls, fields=[]): """All child methods that take a fields parameter should pass the field array and it converts it to the Polarion attribute name using the _cls_suds_map. It uses the python map function over the fields list to return the list of its Polarion attribute name. If the field is a custom field, it adds the customFields. qualifier before the fieldname as is required for searching custom fields. Args: fields - list of fields to convert. If it is not a list, it converts it to one first. default: [] """ p_fields = [] if fields: if not isinstance(fields, list): fields = [fields] # convert given fields to Polarion fields p_fields = ["%s%s" % ( "customFields." if isinstance(cls._cls_suds_map[x], dict) and cls._cls_suds_map[x].get("is_custom", False) else "", cls._cls_suds_map[x] if not isinstance(cls._cls_suds_map[x], dict) else cls._cls_suds_map[x]["field_name"]) for x in fields] # Omit 'URI' from URIFields p_fields = [(x.replace("URI", "")) for x in p_fields] return p_fields
[docs] @classmethod def get_global_roles(cls): """Returns all global roles. Args: None Returns: list of global roles References: Security.\ :security:`getGlobalRoles()` """ return cls.session.security_client.service.getGlobalRoles()
[docs] @classmethod def has_current_user_permission(cls, permission, project_id): """Checks if given permission is granted to the current user. Args: permission: the permission to check. project_id: the id of the project to check the permission in, None to check global permissions. Returns: bool References: Security.hasCurrentUserPermission """ return cls.session.security_client.service.hasCurrentuserPermission( permission, project_id)
[docs] def __init__(self, obj_id=None, suds_object=None): # cls_suds_map must be available for some parameters on the class # level, but gets changed on the instance level and those changes # should not be accessible to other instances. This is the reason # for overwriting it as an instance attribute. self._cls_suds_map = copy.deepcopy(self._cls_suds_map) # _fix_circular_refs is a function that allows objects to contain # circular references by applying the reference only after the class # has been instantiated. Some objects contain references to themselves, # for example a parent attribute. if hasattr(self, "_fix_circular_refs"): self._fix_circular_refs() # if _id_field has not been set in the child class, the obj_id field # cannot be passed as a parameter. if obj_id and not self._id_field: raise PyleroLibException( "{0} only accepts a suds object, not an obj_id".format( self.__class__.__name)) if suds_object: self._suds_object = suds_object else: self._get_suds_object() # initialize all instance attributes as properties # check if the property already exists. If so, use existing. for key in list(self._cls_suds_map.keys()): if not hasattr(self.__class__, key): # require default values for lambda or it evaluates all # variables # at the end of function (self._cls_suds_map[key] was evaluated # as the last key value in the for loop for all defined lambdas # Property Builder, parses _cls_suds_map to build properties: # custom fields: # getter has parameters: # field_name # setter has parameters: # val: the value that the property is set to # field_name # array object fields: # getter has parameters: # field_name # setter has parameters: # val: the value that the property is set to # field_name # object fields: # getter has parameters: # field_name # setter has parameters: # val: the value that the property is set to # field_name # regular fields; # use getattr and setattr if isinstance(self._cls_suds_map[key], dict): if "is_custom" in self._cls_suds_map[key]: setattr(self.__class__, key, property( lambda self, field_name=key: self._custom_getter(field_name), lambda self, val, field_name=key: self._custom_setter(val, field_name))) elif "is_array" in self._cls_suds_map[key]: setattr(self.__class__, key, property( lambda self, field_name=key: self._arr_obj_getter(field_name), lambda self, val, field_name=key: self._arr_obj_setter(val, field_name))) else: setattr(self.__class__, key, property( lambda self, field_name=key: self._obj_getter(field_name), lambda self, val, field_name=key: self._obj_setter(val, field_name))) else: setattr(self.__class__, key, property( # if the attribute doesn't exist in the current object # return None lambda self, suds_key=self._cls_suds_map[key]: getattr(self._suds_object, suds_key, None), lambda self, value, suds_key=self._cls_suds_map[key]: self._regular_setter(value, suds_key))) # after all properties are defined set the id field to the value passed in. if obj_id is not None: setattr(self, self._id_field, obj_id)
def _get_suds_object(self): """Returns the WSDL object as created by the Polarion WSDL factory""" if self._obj_client and self._obj_struct: self._suds_object = getattr(self.session, self._obj_client). \ factory.create(self._obj_struct) else: self._suds_object = None def _obj_getter(self, field_name): """get function for attributes that reference an object. Returns the referenced object. If the WSDL attribute contains a value that value is given to the object as its obj_id. Classes that have an _id_field attribute will return only that attribute when the property is gotten Args: field_name: the field name of the Polarion object to get """ csm = self._cls_suds_map[field_name] named_arg = csm.get("named_arg", "suds_object") suds_field_val = getattr( self._suds_object, csm.get("field_name", ""), None) cls_obj = csm["cls"] if suds_field_val: if named_arg == "uri" and cls_obj._id_field: if suds_field_val: id_re = re.search(self.REGEX_ID, suds_field_val) if id_re: return cls_obj.URI_ID_GET_REPLACE(id_re.group(1)) else: args = {} args[named_arg] = suds_field_val obj = cls_obj(**args) else: obj = cls_obj() if cls_obj._id_field: return getattr(obj, obj._id_field) else: return obj def _obj_setter(self, val, field_name): """set function for attributes that reference an object. It can accept a string, a Pylero object or a raw WSDL object. If a string is given, it is passed in to the object as its obj_id. Args: val: the value that the property is being set to field_name: the field name of the Polarion object to set """ csm = self._cls_suds_map[field_name] suds_field_name = csm["field_name"] enum_id = csm.get("enum_id") enum_override = csm.get("enum_override", []) sync_field = csm.get("sync_field") obj_cls = csm.get("cls") # deepcopy so that changes do not stick add_parms = copy.deepcopy(csm.get("additional_parms", {})) if isinstance(val, basestring): val = self._check_encode(val) if enum_id and val not in enum_override: self.check_valid_field_values( val, enum_id, {}, self._wi_type if hasattr(self, "_wi_type") else None) if not sync_field: sync_field = "_suds_object" if isinstance(val, basestring) or val is None: add_parms[obj_cls._id_field] = val obj = obj_cls(**add_parms) setattr(self._suds_object, suds_field_name, getattr(obj, sync_field)) elif isinstance(val, obj_cls): setattr(self._suds_object, suds_field_name, getattr(val, sync_field)) elif isinstance(val, obj_cls()._suds_object.__class__): obj = obj_cls() if sync_field in obj._cls_suds_map: suds_sync_field = obj._cls_suds_map[sync_field] # if sync_field is given, the attribute will be simple val = getattr(val, suds_sync_field) setattr(self._suds_object, suds_field_name, val) else: raise PyleroLibException("the value {0} is not a valid type". format(val)) def _arr_obj_getter(self, field_name): """get function for attributes that reference an array of objects. The Polarion array object always has a single Python list item which contains a list of the WSDL objects. This function converts each WSDL object to its Pylero object and returns that list Args: field_name: the field name of the Polarion object to get """ csm = self._cls_suds_map[field_name] if getattr(self._suds_object, csm["field_name"], None): obj_lst = [] # ArrayOf Polarion objects have a double list. for inst in getattr(self._suds_object, csm["field_name"])[0]: obj_lst.append(csm["cls"](suds_object=inst)) return obj_lst else: return [] def _arr_obj_setter(self, val, field_name): """set function for attributes that reference an array of objects. It requires a single instance or list of either Pylero or WSDL objects or an empty list. An empty list erases the attribute value. Otherwise it sets the attribute the value passed in. Args: val: the value that the property is set to field_name: the field name of the Polarion object to set """ # TODO: Still needs to be fully tested. Looks like there are some bugs. csm = self._cls_suds_map[field_name] arr_inst = csm.get("arr_cls")() obj_inst = csm.get("cls")() # obj_attach = if not isinstance(val, (list, arr_inst.__class__, arr_inst._suds_object.__class__)): raise PyleroLibException( "{0}s must be a list of {1}").format( csm["field_name"], obj_inst.__class__.__name__) elif not val: setattr( self._suds_object, csm["field_name"], arr_inst._suds_object) elif isinstance(val, arr_inst._suds_object.__class__): setattr(self._suds_object, csm["field_name"], val) elif isinstance(val, arr_inst.__class__): setattr(self._suds_object, csm["field_name"], val._suds_object) else: if isinstance(val, list): # if str values are based in, try instantiating a class with # the vals and then using that list. Then continue processing if isinstance(val[0], basestring): val[0] = self._check_encode(val[0]) val = [csm["cls"](item) for item in val] if isinstance(val[0], obj_inst._suds_object.__class__): setattr(getattr(self._suds_object, csm["field_name"]), csm["inner_field_name"], val) else: setattr(self._suds_object, csm["field_name"], arr_inst._suds_object) for item in val: getattr(getattr(self._suds_object, csm["field_name"]), csm["inner_field_name"]).append( item._suds_object)
[docs] def custom_obj(self): # This returns a custom Polarion object. It can't use the Custom class # as that is a child of this class. return self.session.test_management_client.factory.create( "tns4:Custom")
[docs] def custom_array_obj(self): # This returns a custom Polarion object. It can't use the Custom class # as that is a child of this class. return self.session.test_management_client.factory.create( "tns4:ArrayOfCustom")
def _custom_getter(self, field_name): """Works with custom fields that has attributes stored differently then regular attributes. It first checks if there is a value in the local copy, which may have already been modified by the user. If not, it checks the server for a value. test_steps do not work like all other custom fields, and therefore require specific code. Its values are not saved in the local object. Args: field_name: the field name of the Polarion object to get """ csm = self._cls_suds_map[field_name] if field_name == "test_steps": if self._changed_fields.get("testSteps"): return csm["cls"]( suds_object=self._changed_fields.get("testSteps")) else: test_steps = self.get_test_steps() if test_steps: return test_steps else: if "customFields" not in self._suds_object: self._suds_object.customFields = self.custom_array_obj() cf = self._suds_object.customFields[0] custom_fld = None if cf: # check if the custom field already exists and modify it. match = [x for x in cf if x.key == csm["field_name"]] if match: custom_fld = match[0] if not custom_fld and self.uri: custom_fld = self.get_custom_field( csm["field_name"])._suds_object if custom_fld: if isinstance(custom_fld, basestring): obj = custom_fld elif csm.get("is_array"): obj = [] # ArrayOf Polarion objects have a double list. for inst in custom_fld.value[0]: if csm["cls"]._cls_inner._id_field: item_inst = csm["cls"]._cls_inner(suds_object=inst) obj.append(getattr(item_inst, item_inst._id_field)) else: obj.append(csm["cls"]._cls_inner(suds_object=inst)) elif csm.get("cls"): obj = csm["cls"](suds_object=custom_fld.value) else: obj = custom_fld.value if getattr(obj, "_id_field", None): return getattr(obj, obj._id_field) else: return obj else: return None def _custom_setter(self, val, field_name): """Works with custom fields that has to keep track of values and what changed so that on update it can also update all the custom fields at the same time. Args: val: the value that the property is being set to field_name: the field name of the Polarion object to set """ csm = self._cls_suds_map[field_name] if field_name == "test_steps": if not val: self._changed_fields[csm["field_name"]] = None elif isinstance(val, csm["cls"]): self._changed_fields[csm["field_name"]] = val._suds_object elif isinstance(val, csm["cls"]()._suds_object.__class__): self._changed_fields[csm["field_name"]] = val else: raise PyleroLibException( "The value must be a {0}".format(csm["cls"].__name__)) # move the custom fields to within the object, otherwise each custom # field is a seperate SVN commit. testSteps, does not work unless it # is uploaded using the set_test_steps function. else: cust = self.custom_obj() cust.key = csm["field_name"] if val is None: cust.value = None elif (not csm.get("cls") or isinstance(val, basestring)) \ and not csm.get("is_array"): # if there is no cls specified, val can be a bool, int, ... # if val is a string, it may be used to instantiate the class if isinstance(val, basestring): val = self._check_encode(val) if csm.get("enum_id") and \ val not in csm.get("enum_override", []): # uses deepcopy, to not affect other instances of the class additional_parms = copy.deepcopy( csm.get("additional_parms", {})) self.check_valid_field_values(val, csm.get("enum_id"), additional_parms, csm.get("control")) cust.value = csm["cls"](val)._suds_object if csm.get("cls") \ else val elif csm.get("is_array"): if not isinstance(val, list): raise PyleroLibException("value must be a list") if csm.get("enum_id"): cust.value = csm["cls"]()._suds_object for i in val: if i not in csm.get("enum_override", []): # uses deepcopy, to not affect other instances # of the class additional_parms = copy.deepcopy( csm.get("additional_parms", {})) self.check_valid_field_values( i, csm.get("enum_id"), additional_parms, self._wi_type if hasattr(self,"_wi_type") else None) cust.value[0].append( csm["cls"]._cls_inner(i)._suds_object) elif isinstance(val, csm["cls"]): cust.value = val._suds_object elif isinstance(val, csm["cls"]()._suds_object.__class__): cust.value = val else: raise PyleroLibException( "The value must be of type {0}." .format(csm["cls"].__name__)) if "customFields" not in self._suds_object: self._suds_object.customFields = self.custom_array_obj() cf = self._suds_object.customFields[0] if cf: # check if the custom field already exists and modify it. match = [x for x in cf if x.key == csm["field_name"]] if match: match[0].value = cust.value else: cf.append(cust) self._custom_fields = cf else: self._custom_fields = [cust] def _regular_setter(self, value, field_name): """This setter is used for any attributes that are not Polarion object data types. If the attribute type is a string, then it validates it using the check_encode function Args: value (string): the value that the property is being set to field_name: the field name of the Polarion object to set """ if isinstance(value, basestring): value = self._check_encode(value) setattr(self._suds_object, field_name, value) def _check_encode(self, val): """Validate @val is a UTF-8 because Polarion doesn't support not UTF-8 characters. The only use case that is not taken into account is when an attribute is set directly with a SUDS object. The users have no way of calling this function in this case. Args: val (string): the value that the property is being set to """ try: if not isinstance(val, type(u'')): val = val.decode('utf-8') # replace chr(160) with space return val.replace(u'\xa0', u' ') except UnicodeError as err: raise PyleroLibException( 'String must be UTF-8 encoded. The following error was ' 'raised when converting it to unicode: {0}' .format(err) ) def _get_file_data(self, path): """Method for getting attachment data that can be passed to the soap library. Is used by a number of child classes. Args: path: the file path Returns: base64 encoded binary data. """ f = open(path, "rb") bdata = f.read() f.close() return base64.b64encode(bdata) def _verify_obj(self): # verifies if the object contains a suds object from the server by # checking if the uri field is populated. If no URI it didn't come from # the server if not getattr(self, "uri", None): raise PyleroLibException("There is no {0} loaded".format( self.__class__.__name__))
[docs] def can_add_element_to_key(self, key): """Checks if the current user can add elements to the collection at given key of the current object. Args: key: the key of the field that contains the collection. Returns: bool References: Security.canAddElementToKey """ self._verify_obj() return self.session.security_client.service.canAddElementToKey( self.uri, key)
[docs] def can_delete_instance(self): """Checks if the current user can delete the current object Args: None Returns: bool References: Security.canDeleteInstance """ self._verify_obj() return self.session.security_client.service.canDeleteInstance(self.uri)
[docs] def can_modify_instance(self): """Checks if the current user can modify the current object Args: None Returns: bool References: Security.canModifyInstance """ self._verify_obj() return self.session.security_client.service.canModifyInstance(self.uri)
[docs] def can_modify_key(self, key): """Checks if the current user can modify the field with given key of the current object. Args: key: the key of the field that contains the collection. Returns: bool References: Security.canModifyKey """ self._verify_obj() return self.session.security_client.service.canModifyKey(self.uri, key)
[docs] def can_read_instance(self): """Checks if the current user can read the current object Args: None Returns: bool References: Security.canReadInstance """ self._verify_obj() return self.session.security_client.service.canReadInstance(self.uri)
[docs] def can_read_key(self, key): """Checks if the current user can read the field with given key of the current object. Args: key: the key of the field that contains the collection. Returns: bool References: Security.canReadKey """ self._verify_obj() return self.session.security_client.service.canReadKey(self.uri, key)
[docs] def can_remove_element_from_key(self, key): """Checks if the current user can remove elements from the collection at given key of the current object. Args: key: the key of the field that contains the collection. Returns: bool References: Security.canRemoveElementFromKey """ self._verify_obj() return self.session.security_client.service.canRemoveElementFromKey( self.uri, key)
[docs] def get_location(self): """Returns the location of the current object. In the context of this service the method should be used to get the location of a project(-group). Args: None Returns: location (string) References: Security.getLocationForURI """ self._verify_obj() return self.session.security_client.service.getLocationForURI(self.uri)
[docs] def check_valid_field_values(self, val, enum_id, additional_parms, control=None): """verifies id the value passed in is valid for the enum or object passed in. for example, if we want to see if a valid user is given, this will try to instantiate the User class with the given parameter and additional parms. If it fails, it is not a valid value. Args: val: the value you want to set it to. enum_id: the enumeration or object to validate against additional_parms (dict): parms needed to instantiate class passed in as enum_id control: the control key for the enumeration. default:None """ if isinstance(enum_id, type): try: # try to instantiate the object with the value and additional # parms. If that works, it is a valid value enum_id(val, **additional_parms) except Exception: raise PyleroLibException( "{0} is not a valid value for {1}" .format(val, enum_id.__name__)) else: valid_values = self.get_valid_field_values(enum_id, control) if val not in valid_values: raise PyleroLibException("Acceptable values for {0} are:" "{1}".format(enum_id, valid_values))
[docs] def get_valid_field_values(self, enum_id, control=None): """Gets the available enumeration options. Uses a cache dict because the time to get valid fields from server is time prohibitive. Args: enum_id: The enum code to get values for control: the control key for the enumeration. default:None Returns: Array of EnumOptions References: Tracker.getEnumOptionsForId """ project_id = getattr(self, "project_id", None) or self.default_project enum_base = self._cache["enums"].get(enum_id) enums = None if enum_base: enums = enum_base.get(control) if not enums: enums = self.session.tracker_client.service. \ getEnumOptionsForIdWithControl(project_id, enum_id, control) self._cache["enums"][enum_id]={} self._cache["enums"][enum_id][control] = enums # the _cache contains _suds_object, so the id attribute is used. return [enum.id for enum in enums]
[docs] def reload(self): """Reloads the object with data from the server. This function is useful if the data on the server changed or if a data changing function was called (such as TestRun.add_attachment) Notes: This will overwrite any unsaved data in the object. Args: None Returns: None """ if getattr(self, "uri", None): obj = self.__class__(uri=self.uri) self._suds_object = obj._suds_object