Best Python code snippet using lisa_python
header.py
Source:header.py  
...49        # first get initial size50        argtypes, attr_names, attr_text, num_attributes = func_config()51        func.argtypes = argtypes52        retcode = func(self.fh, attr_names, attr_text, num_attributes)53        warn_or_raise(retcode, func)54        # get actual array size and clean55        array_size = num_attributes.value56        self.spssio.spssFreeAttributes(attr_names, attr_text, num_attributes)57        if array_size == 0:58            return {}59        else:60            # get attributes61            argtypes, attr_names, attr_text, num_attributes = func_config(array_size)62            func.argtypes = argtypes63            retcode = func(self.fh, attr_names, attr_text, num_attributes)64            warn_or_raise(retcode, func)65            # clean66            self.spssio.spssFreeAttributes(attr_names, attr_text, num_attributes)67            attr_names = (x.decode(self.encoding) for x in attr_names[0])68            attr_text = (x.decode(self.encoding) for x in attr_text[0])69            return dict(zip(attr_names, attr_text))70    @file_attributes.setter71    def file_attributes(self, attributes: dict) -> None:72        array_size = len(attributes)73        attr_names = []74        attr_text = []75        for name, text in attributes.items():76            attr_names.append(str(name).encode(self.encoding))77            attr_text.append(str(text).encode(self.encoding))78        attr_names = (c_char_p * array_size)(*attr_names)79        attr_text = (c_char_p * array_size)(*attr_text)80        func = self.spssio.spssSetFileAttributes81        func.argtypes = [82            c_int,83            POINTER(c_char_p * array_size),84            POINTER(c_char_p * array_size),85            c_int,86        ]87        retcode = func(self.fh, attr_names, attr_text, array_size)88        warn_or_raise(retcode, func)89    @property90    def var_names(self) -> list:91        """Variable names92        May return a filtered list when returned as part of a metadata object93        if only a subset of variables are specified to be used (e.g., usecols in read_sav).94        """95        num_vars = self.var_count96        func = self.spssio.spssGetVarNames97        func.argtypes = [98            c_int,99            POINTER(c_int),100            POINTER(POINTER(c_char_p * num_vars)),101            POINTER(POINTER(c_int * num_vars)),102        ]103        _num_vars = c_int()104        var_names = POINTER(c_char_p * num_vars)()105        var_types = POINTER(c_int * num_vars)()106        retcode = func(self.fh, _num_vars, var_names, var_types)107        warn_or_raise(retcode, func)108        var_name_list = [var_names[0][i].decode(self.encoding) for i in range(num_vars)]109        self.spssio.spssFreeVarNames(var_names, var_types, _num_vars)110        return var_name_list111    @property112    def var_types(self) -> dict:113        """Variable types114        May return a filtered dictionary when returned as part of a metadata object115        if only a subset of variables are specified to be used (e.g., usecols in read_sav).116        """117        num_vars = self.var_count118        func = self.spssio.spssGetVarNames119        func.argtypes = [120            c_int,121            POINTER(c_int),122            POINTER(POINTER(c_char_p * num_vars)),123            POINTER(POINTER(c_int * num_vars)),124        ]125        _num_vars = c_int()126        var_names = POINTER(c_char_p * num_vars)()127        var_types = POINTER(c_int * num_vars)()128        retcode = func(self.fh, _num_vars, var_names, var_types)129        warn_or_raise(retcode, func)130        var_types_dict = {131            var_names[0][i].decode(self.encoding): var_types[0][i] for i in range(num_vars)132        }133        self.spssio.spssFreeVarNames(var_names, var_types, _num_vars)134        return var_types_dict135    @var_types.setter136    def var_types(self, var_types: dict):137        for var_name, var_type in var_types.items():138            self._add_var(var_name, var_type)139    def _add_var(self, var_name, var_type=0):140        func = self.spssio.spssSetVarName141        func.argtypes = [c_int, c_char_p, c_int]142        retcode = func(self.fh, var_name.encode(self.encoding), c_int(var_type))143        warn_or_raise(retcode, func, var_name, var_type)144    def _get_var_handle(self, var_name):145        func = self.spssio.spssGetVarHandle146        func.argtypes = [c_int, c_char_p, POINTER(c_double)]147        var_handle = c_double()148        retcode = func(self.fh, var_name.encode(self.encoding), var_handle)149        warn_or_raise(retcode, func, var_name)150        return var_handle151    @property152    def var_handles(self) -> dict:153        """Variable handles references154        Used when calling I/O module procedures that use155        variable handles instead of variable names as arguments156        """157        func = self.spssio.spssGetVarHandle158        func.argtypes = [c_int, c_char_p, POINTER(c_double)]159        var_handles = {}160        for var_name in self.var_names:161            var_handle = c_double()162            retcode = func(self.fh, var_name.encode(self.encoding), var_handle)163            warn_or_raise(retcode, func, var_name)164            var_handles[var_name] = var_handle165        return var_handles166    def _get_var_format(self, var_name):167        func = self.spssio.spssGetVarPrintFormat168        func.argtypes = [c_int, c_char_p, POINTER(c_int), POINTER(c_int), POINTER(c_int)]169        f_type = c_int()170        f_dec = c_int()171        f_width = c_int()172        retcode = func(self.fh, var_name.encode(self.encoding), f_type, f_dec, f_width)173        warn_or_raise(retcode, func, var_name)174        return (f_type.value, f_width.value, f_dec.value)175    @property176    def var_formats_tuple(self) -> dict:177        """Variable formats as tuples in the form (type, width, decimals)178        ex. (5, 8, 2) instead of F8.2179        """180        var_formats = {}181        for var_name in self.var_names:182            var_formats[var_name] = self._get_var_format(var_name)183        return var_formats184    @property185    def var_formats(self) -> dict:186        """Variable formats as strings187        Use var_formats_tuple property for formats as tuples188        """189        var_formats = {}190        for var_name in self.var_names:191            f_type, f_width, f_dec = self._get_var_format(var_name)192            var_formats[var_name] = (193                spss_formats_simple[f_type]194                + str(int(f_width))195                + ("." + str(int(f_dec)) if f_dec else "")196            )197        return var_formats198    @var_formats.setter199    def var_formats(self, var_formats: dict):200        var_names = self.var_names201        for var_name, var_format in var_formats.items():202            if var_name in var_names:203                # convert to tuple in case string type is supplied204                var_format = varformat_to_tuple(var_format)205                f_type, f_width, f_dec = var_format206                for func in (self.spssio.spssSetVarPrintFormat, self.spssio.spssSetVarWriteFormat):207                    func.argtypes = [c_int, c_char_p, c_int, c_int, c_int]208                    retcode = func(209                        self.fh,210                        var_name.encode(self.encoding),211                        c_int(f_type),212                        c_int(f_dec),213                        c_int(f_width),214                    )215                    warn_or_raise(retcode, func, var_name, var_format)216    @property217    def var_measure_levels(self) -> dict:218        """Variable measure levels219        Measure levels are returned as strings.220        When setting, input accepts either strings or numerics.221         - 0 = unknown222         - 1 = nominal223         - 2 = ordinal224         - 3 = scale225        """226        func = self.spssio.spssGetVarMeasureLevel227        func.argtypes = [c_int, c_char_p, POINTER(c_int)]228        var_levels = {}229        for var_name in self.var_names:230            level = c_int()231            retcode = func(self.fh, var_name.encode(self.encoding), level)232            warn_or_raise(retcode, func, var_name)233            var_levels[var_name] = measure_levels_str[level.value]234        return var_levels235    @var_measure_levels.setter236    def var_measure_levels(self, var_measure_levels: dict):237        var_names = self.var_names238        func = self.spssio.spssSetVarMeasureLevel239        func.argtypes = [c_int, c_char_p, c_int]240        for var_name, measure_level in var_measure_levels.items():241            measure_level = measure_levels.get(str(measure_level).lower(), measure_level)242            if var_name in var_names:243                retcode = func(self.fh, var_name.encode(self.encoding), c_int(measure_level))244                warn_or_raise(retcode, func, var_name, measure_level)245    @property246    def var_alignments(self) -> dict:247        """Variable alignments248        Alignments are returned as strings.249        When setting, input accepts either strings or numerics.250         - 0 = left251         - 1 = right252         - 2 = center253        """254        func = self.spssio.spssGetVarAlignment255        func.argtypes = [c_int, c_char_p, POINTER(c_int)]256        var_alignments = {}257        for var_name in self.var_names:258            align = c_int()259            retcode = func(self.fh, var_name.encode(self.encoding), align)260            warn_or_raise(retcode, func, var_name)261            var_alignments[var_name] = alignments_str[align.value]262        return var_alignments263    @var_alignments.setter264    def var_alignments(self, var_alignments: dict):265        var_names = self.var_names266        func = self.spssio.spssSetVarAlignment267        func.argtypes = [c_int, c_char_p, c_int]268        for var_name, align in var_alignments.items():269            align = alignments.get(str(align).lower(), align)270            if var_name in var_names:271                retcode = func(self.fh, var_name.encode(self.encoding), align)272                warn_or_raise(retcode, func, var_name, align)273    @property274    def var_column_widths(self) -> dict:275        """Column display widths276        Manually set column widths or specify 0 to use SPSS' algorithm to assign a width277        """278        func = self.spssio.spssGetVarColumnWidth279        func.argtypes = [c_int, c_char_p, POINTER(c_int)]280        widths = {}281        for var_name in self.var_names:282            column_width = c_int()283            retcode = func(self.fh, var_name.encode(self.encoding), column_width)284            warn_or_raise(retcode, func, var_name)285            widths[var_name] = column_width.value286        return widths287    @var_column_widths.setter288    def var_column_widths(self, var_column_widths: dict):289        var_names = self.var_names290        func = self.spssio.spssSetVarColumnWidth291        func.argtypes = [c_int, c_char_p, c_int]292        for var_name, column_width in var_column_widths.items():293            if var_name in var_names:294                retcode = func(self.fh, var_name.encode(self.encoding), column_width)295                warn_or_raise(retcode, func, var_name, column_width)296    @property297    def var_labels(self) -> dict:298        """Variable labels"""299        len_buff = SPSS_MAX_VARLABEL + 1300        buffer = create_string_buffer(len_buff)301        func = self.spssio.spssGetVarLabelLong302        func.argtypes = [c_int, c_char_p, c_char_p, c_int, POINTER(c_int)]303        var_labels = {}304        for var_name in self.var_names:305            len_label = c_int()306            retcode = func(self.fh, var_name.encode(self.encoding), buffer, len_buff, len_label)307            warn_or_raise(retcode, func, var_name)308            var_labels[var_name] = buffer.value.decode(self.encoding)309        return var_labels310    @var_labels.setter311    def var_labels(self, labels: dict):312        var_names = self.var_names313        func = self.spssio.spssSetVarLabel314        func.argtypes = [c_int, c_char_p, c_char_p]315        for var_name, var_label in labels.items():316            if var_name in var_names:317                retcode = func(318                    self.fh, var_name.encode(self.encoding), var_label.encode(self.encoding)319                )320                warn_or_raise(retcode, func, var_name, var_label)321    @property322    def var_roles(self) -> dict:323        """Variable roles324        Roles are returned as strings.325        When setting, input accepts either strings or numerics.326         - 0 = input327         - 1 = target328         - 2 = both329         - 3 = none330         - 4 = partition331         - 5 = split332         - 6 = frequency333         - 7 = recordid334        """335        func = self.spssio.spssGetVarRole336        func.argtypes = [c_int, c_char_p, POINTER(c_int)]337        var_roles = {}338        for var_name in self.var_names:339            role = c_int()340            retcode = func(self.fh, var_name.encode(self.encoding), role)341            warn_or_raise(retcode, func, var_name)342            var_roles[var_name] = roles_str[role.value]343        return var_roles344    @var_roles.setter345    def var_roles(self, var_roles: dict):346        var_names = self.var_names347        func = self.spssio.spssSetVarRole348        func.argtypes = [c_int, c_char_p, c_int]349        for var_name, role in var_roles.items():350            role = roles.get(str(role).lower(), role)351            if var_name in var_names:352                retcode = func(self.fh, var_name.encode(self.encoding), role)353                warn_or_raise(retcode, func, var_name, role)354    def _get_var_n_value_labels(self, var_name):355        func = self.spssio.spssGetVarNValueLabels356        def func_config(var_name, size=0):357            argtypes = [358                c_int,359                c_char_p,360                POINTER(POINTER(c_double * size)),361                POINTER(POINTER(c_char_p * size)),362                POINTER(c_int),363            ]364            values_arr = POINTER((c_double * size))()365            labels_arr = POINTER((c_char_p * size))()366            num_labels = c_int()367            return argtypes, var_name, values_arr, labels_arr, num_labels368        # initial function call to get number of labels369        argtypes, var_name, values_arr, labels_arr, num_labels = func_config(var_name)370        func.argtypes = argtypes371        retcode = func(self.fh, var_name.encode(self.encoding), values_arr, labels_arr, num_labels)372        if retcode > 0:373            self.spssio.spssFreeVarNValueLabels(values_arr, labels_arr, num_labels)374            warn_or_raise(retcode, func, var_name)375        elif num_labels.value > 0:376            # if function call was successful and variable has value labels377            argtypes, var_name, values_arr, labels_arr, num_labels = func_config(378                var_name, num_labels.value379            )380            func.argtypes = argtypes381            retcode = func(382                self.fh, var_name.encode(self.encoding), values_arr, labels_arr, num_labels383            )384            warn_or_raise(retcode, func, var_name)385            value_labels = {386                values_arr[0][i]: labels_arr[0][i].decode(self.encoding)387                for i in range(num_labels.value)388            }389            self.spssio.spssFreeVarNValueLabels(values_arr, labels_arr, num_labels)390            return value_labels391        else:392            return {}393    def _get_var_c_value_labels(self, var_name):394        func = self.spssio.spssGetVarCValueLabels395        def func_config(var_name, size=0):396            argtypes = [397                c_int,398                c_char_p,399                POINTER(POINTER(c_char_p * size)),400                POINTER(POINTER(c_char_p * size)),401                POINTER(c_int),402            ]403            values_arr = POINTER((c_char_p * size))()404            labels_arr = POINTER((c_char_p * size))()405            num_labels = c_int()406            return argtypes, var_name, values_arr, labels_arr, num_labels407        # initial function call to get number of labels408        argtypes, var_name, values_arr, labels_arr, num_labels = func_config(var_name)409        func.argtypes = argtypes410        retcode = func(self.fh, var_name.encode(self.encoding), values_arr, labels_arr, num_labels)411        if retcode > 0:412            self.spssio.spssFreeVarCValueLabels(values_arr, labels_arr, num_labels)413            warn_or_raise(retcode, func, var_name)414        elif num_labels.value > 0:415            # if function call was successful and variable has value labels416            argtypes, var_name, values_arr, labels_arr, num_labels = func_config(417                var_name, num_labels.value418            )419            func.argtypes = argtypes420            retcode = func(421                self.fh, var_name.encode(self.encoding), values_arr, labels_arr, num_labels422            )423            warn_or_raise(retcode, func, var_name)424            value_labels = {425                values_arr[0][i]426                .decode(self.encoding)427                .rstrip(): labels_arr[0][i]428                .decode(self.encoding)429                for i in range(num_labels.value)430            }431            self.spssio.spssFreeVarCValueLabels(values_arr, labels_arr, num_labels)432            return value_labels433        else:434            return {}435    @property436    def var_value_labels(self) -> dict:437        """Variable value labels438        Nested dictionary of variables with their value labels (if defined) as sub-dictionaries439        Note: value labels only work for numeric and short string variables (length <= 8)440        """441        var_value_labels = {}442        for var_name, var_type in self.var_types.items():443            if var_type == 0:444                var_value_labels[var_name] = self._get_var_n_value_labels(var_name)445            elif var_type <= 8:446                var_value_labels[var_name] = self._get_var_c_value_labels(var_name)447        return {k: v for k, v in var_value_labels.items() if v}448    def _set_var_n_value_label(self, var_name, value, label):449        func = self.spssio.spssSetVarNValueLabel450        func.argtypes = [c_int, c_char_p, c_double, c_char_p]451        retcode = func(452            self.fh, var_name.encode(self.encoding), c_double(value), label.encode(self.encoding)453        )454        warn_or_raise(retcode, func, var_name, value, label)455    def _set_var_c_value_label(self, var_name, value, label):456        func = self.spssio.spssSetVarCValueLabel457        func.argtypes = [c_int, c_char_p, c_char_p, c_char_p]458        retcode = func(459            self.fh,460            var_name.encode(self.encoding),461            value.encode(self.encoding),462            label.encode(self.encoding),463        )464        warn_or_raise(retcode, func, var_name, value, label)465    @var_value_labels.setter466    def var_value_labels(self, var_value_labels: dict):467        var_types = self.var_types468        for var_name, value_labels in var_value_labels.items():469            var_type = var_types.get(var_name)470            if var_type is not None:471                if var_type:472                    func = self._set_var_c_value_label473                else:474                    func = self._set_var_n_value_label475                for value, label in value_labels.items():476                    func(var_name, value, label)477    @property478    def mrsets_count(self) -> int:479        """Number of multi response set definitions480        Needed if using spssGetMultRespDefByIndex.481        Otherwise, len(mrsets) should be equivalent.482        """483        func = self.spssio.spssGetMultRespCount484        func.argtypes = [c_int, POINTER(c_int)]485        count = c_int()486        retcode = func(self.fh, count)487        warn_or_raise(retcode, func)488        return count.value489    def _parse_mrset_c(self, attr: str) -> dict:490        d = {}491        d["type"] = "C"492        d["is_dichotomy"] = False493        idx = 2494        d["value_length"], d["counted_value"] = None, None495        label_length = attr[idx:].split(" ", 1)[0]496        idx += len(label_length) + 1497        d["label"] = attr[idx:][: int(label_length.strip())]498        idx += len(d["label"]) + 1499        d["variable_list"] = attr[idx:].split()500        return d501    def _parse_mrset_d(self, attr: str) -> dict:502        d = {}503        d["type"] = "D"504        d["is_dichotomy"] = True505        idx = 1506        value_length = attr[idx:].split(" ", 1)[0]507        idx += len(value_length) + 1508        d["counted_value"] = attr[idx:][: int(value_length.strip())]509        idx += len(d["counted_value"]) + 1510        label_length = attr[idx:].split(" ", 1)[0]511        idx += len(label_length) + 1512        d["label"] = attr[idx:][: int(label_length.strip())]513        idx += len(d["label"]) + 1514        d["variable_list"] = attr[idx:].split()515        return d516    def _parse_mrset_e(self, attr: str) -> dict:517        d = {}518        d["type"] = "E"519        d["is_dichotomy"] = True520        d["use_category_labels"] = True521        d["use_first_var_label"] = attr[3] == "1"522        idx = 4 + d["use_first_var_label"]523        value_length = attr[idx:].split(" ", 1)[0]524        idx += len(value_length) + 1525        d["counted_value"] = attr[idx:][: int(value_length.strip())]526        idx += len(d["counted_value"]) + 1527        label_length = attr[idx:].split(" ", 1)[0]528        idx += len(label_length) + 1529        d["label"] = attr[idx:][: int(label_length.strip())]530        idx += len(d["label"]) + 1531        d["variable_list"] = attr[idx:].split()532        return d533    @property534    def mrsets(self) -> dict:535        """Multi response set definitions536        Multi response sets contain the following attributes537         - label : set label538         - is_dichotomy : whether set is dichotomous (True) or Category (False)539         - counted_value : counted value for dichotomous sets540         - use_category_labels : whether to use counted value labels instead of variable labels541         - use_first_var_label : whether to use first var label as set label542         - variable_list : list of variables in the set543        Notes544        -----545        mrset name must begin with a "$".546        variable_list is the only required attribute.547        However, if this is the only included attribute, then is_dichotomy is assumed to be False.548        If is_dichotomy is True, counted_value must be specified.549        If is_dichotomy is None and counted_value is not None, is_dichotomy is assumed to be True.550        Numeric dichotomous sets only accept integers for a counted value.551        use_category_labels is only applicable for dichotomous sets.552        Setting this to True turns the set into an "extended" mrset definition.553        use_first_var_label is only applicable when use_category_labels is True.554        Specifying a set label when use_first_var_label is True might result in an invalid mrset definition.555        Examples556        --------557        Category (C) Set::558            {"$mc_mrset": {559                "label": "This is an MC set",560                "variable_list": ["var1", "var2", "var3"]561            }}562        Dichotomous (D) Set::563            {"$md_mrset": {564                "label": "This is an MD set",565                "counted_value": 1,566                "variable_list": ["resp1", "resp2", "resp3"]567            }}568        Dichotomous (E - Extended) Set::569            {"$md_mrset": {570                "counted_value": 1,571                "use_category:labels": True,572                "use_first_var_label": True,573                "variable_list": ["cat1", "cat2", "cat3"]574            }}575        """576        mrsets_dict = {}577        func = self.spssio.spssGetMultRespDefsEx578        func.argtypes = [c_int, POINTER(c_char_p)]579        mrsets_string = c_char_p()580        retcode = func(self.fh, mrsets_string)581        if retcode > 0:582            self.spssio.spssFreeMultRespDefs(mrsets_string)583            warn_or_raise(retcode, func)584        elif mrsets_string:585            mrsets = mrsets_string.value.decode(self.encoding)  # pylint: disable=no-member586            mrsets = mrsets.strip().split("\n")587            mrsets = [x.split("=", 1) for x in mrsets]588            for mrset in mrsets:589                d = {}590                setname, attr = mrset591                if attr[0].upper() == "C":592                    mrsets_dict[setname] = self._parse_mrset_c(attr)593                elif attr[0].upper() == "D":594                    mrsets_dict[setname] = self._parse_mrset_d(attr)595                elif attr[0].upper() == "E":596                    mrsets_dict[setname] = self._parse_mrset_e(attr)597        # clean598        self.spssio.spssFreeMultRespDefs(mrsets_string)599        warn_or_raise(retcode, func)600        if len(mrsets_dict):601            var_types = self.var_types602            for mrset, d in mrsets_dict.items():603                if d["counted_value"] is not None and var_types[d["variable_list"][0]] == 0:604                    d["counted_value"] = int(d["counted_value"])605        return mrsets_dict606    @mrsets.setter607    def mrsets(self, mrsets: dict):608        for mrset_name, mrset_attr in mrsets.items():609            self._add_mrset(mrset_name, mrset_attr)610    def _add_mrset(self, mrset_name: str, mrset_attr: dict) -> None:611        var_types = self.var_types612        is_dichotomy = mrset_attr.get("is_dichotomy")613        counted_value = mrset_attr.get("counted_value")614        # infer is_dichotomy when not explicitly specified615        if is_dichotomy is None:616            is_dichotomy = counted_value is not None617        # determine if set is numeric618        is_numeric = isinstance(counted_value, (int, float))619        encoded_vars = []620        for var in mrset_attr.get("variable_list", []):621            if var in var_types:622                encoded_vars.append(var.encode(self.encoding))623        num_vars = len(encoded_vars)624        class MRSetStruct(Structure):625            pass626        MRSetStruct._fields_ = [627            ("szMrSetName", c_char * int((SPSS_MAX_VARNAME + 1))),628            ("szMrSetLabel", c_char * int((SPSS_MAX_VARLABEL + 1))),629            ("qIsDichotomy", c_int),630            ("qIsNumeric", c_int),631            ("qUseCategoryLabels", c_int),632            ("qUseFirstVarLabel", c_int),633            ("Reserved", c_int * 14),634            ("nCountedValue", c_long),635            ("pszCountedValue", c_char_p),636            ("ppszVarNames", POINTER(c_char_p * num_vars)),637            ("nVariables", c_int),638        ]639        func = self.spssio.spssAddMultRespDefExt640        func.argtypes = [c_int, POINTER(MRSetStruct)]641        set_name = mrset_name.encode(self.encoding)642        set_label = mrset_attr.get("label", "").encode(self.encoding)643        is_dichotomy = int(is_dichotomy)644        is_numeric = int(is_numeric)645        use_category_labels = int(is_dichotomy and mrset_attr.get("use_category_labels", False))646        use_first_var_label = int(647            is_dichotomy and use_category_labels and mrset_attr.get("use_first_var_label", False)648        )649        reserved = (c_int * 14)(0)650        n_counted_value = 0 if not is_numeric else int(counted_value)651        c_counted_value = ("" if (is_numeric or not is_dichotomy) else counted_value).encode(652            self.encoding653        )654        var_names = (c_char_p * num_vars)(*encoded_vars)655        args = (656            set_name,657            set_label,658            is_dichotomy,659            is_numeric,660            use_category_labels,661            use_first_var_label,662            reserved,663            n_counted_value,664            c_counted_value,665            pointer(var_names),666            num_vars,667        )668        mrset_struct = MRSetStruct(*args)669        retcode = func(self.fh, byref(mrset_struct))670        warn_or_raise(retcode, func, (mrset_name, mrset_attr, args))671    @property672    def case_size(self) -> int:673        """Record case size (in bytes)674        Raw number of bytes for a single case record.675        It can be calculated manually by adding all variable types676        rounded up to the nearest multiple of 8.677        This is the buffer size used to read a whole case record at once.678        It is not necessily the number of bytes used to store a679        case record on disk (depending on compression).680        """681        func = self.spssio.spssGetCaseSize682        func.argtypes = [c_int, POINTER(c_long)]683        case_size = c_long()684        retcode = func(self.fh, case_size)685        warn_or_raise(retcode, func)686        return case_size.value687    @property688    def case_weight_var(self) -> str:689        """Case weight variable690        Variable set as the "weight" variable in SPSS.691        Must be a scale numeric variable.692        """693        func = self.spssio.spssGetCaseWeightVar694        case_weight_var = create_string_buffer(SPSS_MAX_VARNAME + 1)695        retcode = func(self.fh, case_weight_var)696        warn_or_raise(retcode, func)697        return case_weight_var.value.decode(self.encoding)698    @case_weight_var.setter699    def case_weight_var(self, var_name: str):700        func = self.spssio.spssSetCaseWeightVar701        func.argtypes = [c_int, c_char_p]702        retcode = func(self.fh, var_name.encode(self.encoding))703        warn_or_raise(retcode, func, var_name)704    def _get_var_n_missing_values(self, var_name):705        func = self.spssio.spssGetVarNMissingValues706        func.argtypes = [707            c_int,708            c_char_p,709            POINTER(c_int),710            POINTER(c_double),711            POINTER(c_double),712            POINTER(c_double),713        ]714        missing_format = c_int()715        val_1, val_2, val_3 = c_double(), c_double(), c_double()716        retcode = func(717            self.fh, var_name.encode(self.encoding), missing_format, val_1, val_2, val_3718        )719        warn_or_raise(retcode, func, var_name)720        missing_format = missing_format.value721        missing_values = [val_1.value, val_2.value, val_3.value]722        return (missing_format, missing_values)723    def _get_var_c_missing_values(self, var_name, var_type):724        """Will not return missing values if variable type > 8 (ex. A25)"""725        func = self.spssio.spssGetVarCMissingValues726        func.argtypes = [c_int, c_char_p, POINTER(c_int), c_char_p, c_char_p, c_char_p]727        missing_format = c_int()728        val_1 = create_string_buffer(var_type + 1)729        val_2 = create_string_buffer(var_type + 1)730        val_3 = create_string_buffer(var_type + 1)731        retcode = func(732            self.fh, var_name.encode(self.encoding), missing_format, val_1, val_2, val_3733        )734        warn_or_raise(retcode, func, var_name)735        missing_format = missing_format.value736        missing_values = [737            val_1.value.decode(self.encoding).rstrip(),738            val_2.value.decode(self.encoding).rstrip(),739            val_3.value.decode(self.encoding).rstrip(),740        ]741        return (missing_format, missing_values)742    @property743    def var_missing_values(self) -> dict:744        """Missing values745        Missing value definitions may contain three keys746         1. lo = Low value used in missing range747         2. hi = high value used in missing range748         3. values = list of discrete values set as user missing749        For missing ranges, the following keywords can be used inplace of numeric values750         - low = -inf, lo, low, lowest751         - high = inf, hi, high, highest752        """753        var_missing_values = {}754        for var_name, var_type in self.var_types.items():755            if var_type:756                missing_format, missing_values = self._get_var_c_missing_values(var_name, var_type)757            else:758                missing_format, missing_values = self._get_var_n_missing_values(var_name)759            if missing_format == SPSS_NO_MISSVAL:760                var_missing_values[var_name] = None761            elif missing_format in [SPSS_ONE_MISSVAL, SPSS_TWO_MISSVAL, SPSS_THREE_MISSVAL]:762                var_missing_values[var_name] = {"values": missing_values[:missing_format]}763            elif missing_format in [SPSS_MISS_RANGE, SPSS_MISS_RANGEANDVAL]:764                low, high = missing_values[:2]765                low = float("-inf") if low <= self.low_value else low766                high = float("inf") if high >= self.high_value else high767                var_missing_values[var_name] = {"lo": low, "hi": high}768                if missing_format == SPSS_MISS_RANGEANDVAL:769                    var_missing_values[var_name]["values"] = missing_values[2:3]770        return {k: v for k, v in var_missing_values.items() if v}771    @var_missing_values.setter772    def var_missing_values(self, var_missing_values: dict):773        var_types = self.var_types774        for var_name, missing_values in var_missing_values.items():775            var_type = var_types.get(var_name)776            if var_type:777                func = self.spssio.spssSetVarCMissingValues778                func.argtypes = [c_int, c_char_p, c_int, c_char_p, c_char_p, c_char_p]779                discrete_values = missing_values.get("values", [])780                missing_format = min(3, len(discrete_values))781                val_1 = "" if missing_format < SPSS_ONE_MISSVAL else discrete_values[0]782                val_2 = "" if missing_format < SPSS_TWO_MISSVAL else discrete_values[1]783                val_3 = "" if missing_format < SPSS_THREE_MISSVAL else discrete_values[2]784                retcode = func(785                    self.fh,786                    var_name.encode(self.encoding),787                    c_int(missing_format),788                    val_1.encode(self.encoding),789                    val_2.encode(self.encoding),790                    val_3.encode(self.encoding),791                )792                warn_or_raise(retcode, func, var_name)793            elif var_type == 0:794                func = self.spssio.spssSetVarNMissingValues795                func.argtypes = [c_int, c_char_p, c_int, c_double, c_double, c_double]796                low = missing_values.get("lo")797                high = missing_values.get("hi")798                discrete_values = missing_values.get("values", [])799                if low is not None and high is not None:800                    if str(low) in ["-inf", "lo", "low", "lowest"] or low <= self.low_value:801                        low = self.low_value802                    if str(high) in ["inf", "hi", "high", "highest"] or high >= self.high_value:803                        high = self.high_value804                    val_1 = low805                    val_2 = high806                    if len(discrete_values):807                        missing_format = SPSS_MISS_RANGEANDVAL808                        val_3 = discrete_values[0]809                    else:810                        missing_format = SPSS_MISS_RANGE811                        val_3 = self.sysmis812                else:813                    missing_format = min(3, len(discrete_values))814                    val_1 = (815                        self.sysmis if missing_format < SPSS_ONE_MISSVAL else discrete_values[0]816                    )817                    val_2 = (818                        self.sysmis if missing_format < SPSS_TWO_MISSVAL else discrete_values[1]819                    )820                    val_3 = (821                        self.sysmis if missing_format < SPSS_THREE_MISSVAL else discrete_values[2]822                    )823                retcode = func(824                    self.fh,825                    var_name.encode(self.encoding),826                    c_int(missing_format),827                    c_double(val_1),828                    c_double(val_2),829                    c_double(val_3),830                )831                warn_or_raise(retcode, func, var_name)832    def _get_var_attributes(self, var_name):833        """Get attributes for a single variable"""834        func = self.spssio.spssGetVarAttributes835        clean = self.spssio.spssFreeAttributes836        def func_config(array_size=0):837            argtypes = [838                c_int,839                c_char_p,840                POINTER(POINTER(c_char_p * array_size)),841                POINTER(POINTER(c_char_p * array_size)),842                POINTER(c_int),843            ]844            attr_names = POINTER(c_char_p * array_size)()845            attr_text = POINTER(c_char_p * array_size)()846            num_attributes = c_int()847            return argtypes, attr_names, attr_text, num_attributes848        # first get initial size849        argtypes, attr_names, attr_text, num_attributes = func_config()850        func.argtypes = argtypes851        retcode = func(852            self.fh, var_name.encode(self.encoding), attr_names, attr_text, num_attributes853        )854        warn_or_raise(retcode, func)855        # get actual array size and clean856        array_size = num_attributes.value857        retcode = clean(attr_names, attr_text, num_attributes)858        warn_or_raise(retcode, clean)859        if array_size == 0:860            return {}861        else:862            # get attributes863            argtypes, attr_names, attr_text, num_attributes = func_config(array_size)864            func.argtypes = argtypes865            retcode = func(866                self.fh, var_name.encode(self.encoding), attr_names, attr_text, num_attributes867            )868            warn_or_raise(retcode, func)869            # clean870            retcode = clean(attr_names, attr_text, num_attributes)871            warn_or_raise(retcode, clean)872            attr_names = (x.decode(self.encoding) for x in attr_names[0])873            attr_text = (x.decode(self.encoding) for x in attr_text[0])874            return dict(zip(attr_names, attr_text))875    @property876    def var_attributes(self) -> dict:877        """Variable attributes878        These are arbitrary variable properties,879        analagous to file attributes"""880        var_attributes = {}881        for var_name in self.var_names:882            attributes = self._get_var_attributes(var_name)883            if attributes:884                var_attributes[var_name] = attributes885        return var_attributes886    @var_attributes.setter887    def var_attributes(self, var_attributes: dict):888        func = self.spssio.spssSetVarAttributes889        for var_name, attributes in var_attributes.items():890            array_size = len(attributes)891            attr_names = []892            attr_text = []893            for name, text in attributes.items():894                attr_names.append(str(name).encode(self.encoding))895                attr_text.append(str(text).encode(self.encoding))896            attr_names = (c_char_p * array_size)(*attr_names)897            attr_text = (c_char_p * array_size)(*attr_text)898            func.argtypes = [899                c_int,900                c_char_p,901                POINTER(c_char_p * array_size),902                POINTER(c_char_p * array_size),903                c_int,904            ]905            retcode = func(906                self.fh, var_name.encode(self.encoding), attr_names, attr_text, array_size907            )908            warn_or_raise(retcode, func, var_name)909    def _get_var_compat_name(self, var_name):910        "Returns 8 byte compatible variable name"911        func = self.spssio.spssGetVarCompatName912        func.argtypes = [c_int, c_char_p, c_char_p]913        var_compat_name = create_string_buffer(SPSS_MAX_SHORTVARNAME + 1)914        retcode = func(self.fh, var_name.encode(self.encoding), var_compat_name)915        warn_or_raise(retcode, func, var_name)916        return var_compat_name.value.decode(self.encoding).strip()917    @property918    def var_compat_names(self) -> dict:919        """Short (8-byte) variable names920        Dictionary of variable names with their "compatible" short 8-byte counterparts921        """922        var_compat_names = {}923        for var_name in self.var_names:924            var_compat_names[var_name] = self._get_var_compat_name(var_name)925        return var_compat_names926    @property927    def var_sets(self) -> dict:928        """Variable sets929        These are NOT multi response sets. These variable sets are groupings930        of variables that can be selected in the SPSS application as a sort of view filter.931        SPSS apparently may use the 8 byte compatible variable names for this property.932        It's currently not possible to obtain the auto-generated compatible names933        until the dictionary is committed, which means setting this property potentially934        requires first comitting a dictionary with all variables, and then rewriting it935        after obtaining the compatible variable names.936        Set names when created in the normal SPSS application allow spaces and special characters.937        However, The I/O module returns an SPSS_INVALID_VARSETDEF error when these are included.938        When an "=" sign is included in the set name, the set name is truncated.939        """940        short_to_long_var_names = {941            var_compat_name: var_name942            for var_name, var_compat_name in self.var_compat_names.items()943        }944        func = self.spssio.spssGetVariableSets945        clean = self.spssio.spssFreeVariableSets946        func.argtypes = [c_int, POINTER(c_char_p)]947        var_sets_string = c_char_p()948        retcode = func(self.fh, var_sets_string)949        warn_or_raise(retcode, func)950        var_sets_dict = {}951        if retcode not in [SPSS_NO_VARSETS, SPSS_EMPTY_VARSETS]:952            var_sets = var_sets_string.value.decode(self.encoding)  # pylint: disable=no-member953            var_sets = var_sets.strip().split("\n")954            for var_set in var_sets:955                try:956                    set_name, var_list = var_set.split("=", maxsplit=1)957                except ValueError:958                    pass959                else:960                    var_list = var_list.strip().split()961                    var_sets_dict[set_name] = [962                        short_to_long_var_names.get(var, var) for var in var_list963                    ]964        retcode = clean(var_sets_string)965        warn_or_raise(retcode, clean)966        return var_sets_dict967    @var_sets.setter968    def var_sets(self, var_sets: dict):969        if not var_sets:970            return971        func = self.spssio.spssSetVariableSets972        func.argtypes = [c_int, c_char_p]973        var_set_defs = []974        for set_name, var_list in var_sets.items():975            set_name_fixed = set_name if "=" not in set_name else set_name[: set_name.find("=")]976            var_list_string = " ".join(var_list)977            var_set_defs.append(f"{set_name_fixed}= {var_list_string}")978        var_sets_string = "\n".join(var_set_defs)979        retcode = func(self.fh, var_sets_string.encode(self.encoding))980        warn_or_raise(retcode, func)981    def commit_header(self):982        """Finalize metadata983        This function is used to finalize the header information before writing data.984        Once this function is called, no further metadata modification is allowed.985        """986        func = self.spssio.spssCommitHeader987        retcode = func(self.fh)...spssfile.py
Source:spssfile.py  
...148    def interface_encoding(self, unicode: bool):149        func = self.spssio.spssSetInterfaceEncoding150        func.argtypes = [c_int]151        retcode = func(c_int(int(unicode)))152        warn_or_raise(retcode, func)153        return154    @property155    def file_encoding(self) -> str:156        """File encoding reported by I/O module"""157        func = self.spssio.spssGetFileEncoding158        psz_encoding = create_string_buffer(SPSS_MAX_ENCODING + 1)159        retcode = func(self.fh, psz_encoding)160        warn_or_raise(retcode, func)161        return psz_encoding.value.decode(self.encoding)162    def set_locale(self, locale: str) -> str:163        """Set I/O module to a specific locale"""164        func = self.spssio.spssSetLocale165        func.argtypes = [c_int, c_char_p]166        func.restype = c_char_p167        result = func(lc.LC_ALL, locale.encode(self.encoding))168        if result:169            return result.decode(self.encoding)170        else:171            warnings.warn(172                "Failed to set locale to: "173                + locale174                + ". "175                + "Current locale is: "176                + ".".join(lc.getlocale()),177                stacklevel=2,178            )179            return ".".join(lc.getlocale())180    @property181    def is_compatible_encoding(self) -> bool:182        """Check encoding compatibility183        From I/O module documentation: "This function determines whether the file's encoding is compatible with the current interface encoding.184        The result value ... will be false when reading a code page file in UTF-8 mode, when reading185        a UTF-8 file in code page mode when reading a code page file encoded in other than the current locale's186        code page, or when reading a file with numbers represented in reverse bit order. If the encoding is187        incompatible, data stored in the file by other applications, particularly Data Entry for Windows, may be188        unreliable."189        """190        func = self.spssio.spssIsCompatibleEncoding191        func.argtypes = [c_int, POINTER(c_int)]192        b_compatible = c_int()193        retcode = func(self.fh, b_compatible)194        warn_or_raise(retcode, func)195        return bool(b_compatible.value)196    def open(self) -> int:197        """Open file198        Returns file handle that is used for most other I/O module functions.199        Notes200        -----201        Filenames are always encoded in UTF-8 regardless of interface mode and locale settings.202        This is to avoid issues where a filename uses special characters that aren't available203        in the encoding defined by the file itself. For example, a Windows-1252 .sav file204        which uses Chinese (or other special multibyte characters) in its filename.205        """206        with open(self.filename, self.mode) as f:207            fh = c_int(f.fileno())208        filename_adjusted = os.path.expanduser(os.path.abspath(self.filename))209        filename_encoded = filename_adjusted.encode("utf-8")210        func = self._modes[self.mode]["open"]211        retcode = func(filename_encoded, byref(fh))212        warn_or_raise(retcode, func)213        return fh214    def close(self):215        """Close file"""216        func = self._modes[self.mode]["close"]217        retcode = func(self.fh)218        warn_or_raise(retcode, func)219    @property220    def compression(self) -> int:221        """Compression level222        - 0 = No compression223        - 1 = SAV224        - 2 = ZSAV225        """226        func = self.spssio.spssGetCompression227        func.argtypes = [c_int, POINTER(c_int)]228        comp_switch = c_int()229        retcode = func(self.fh, comp_switch)230        warn_or_raise(retcode, func)231        return comp_switch.value232    @compression.setter233    def compression(self, comp_switch=1):234        func = self.spssio.spssSetCompression235        retcode = func(self.fh, c_int(comp_switch))236        warn_or_raise(retcode, func)237    @property238    def release_info(self) -> dict:239        """Basic file information240        - release number241        - release subnumber242        - fixpack number243        - machine code244        - floating-point representation code245        - compression scheme code246        - big/little-endian code247        - character representation code248        """249        fields = [250            "release number",251            "release subnumber",252            "fixpack number",253            "machine code",254            "floating-point representation code",255            "compression scheme code",256            "big/little-endian code",257            "character representation code",258        ]259        rel_info_arr = (c_int * len(fields))()260        func = self.spssio.spssGetReleaseInfo261        retcode = func(self.fh, rel_info_arr)262        warn_or_raise(retcode, func)263        return dict([(item, rel_info_arr[i]) for i, item in enumerate(fields)])264    @property265    def var_count(self) -> int:266        """Number of variables"""267        func = self.spssio.spssGetNumberofVariables268        func.argtypes = [c_int, POINTER(c_long)]269        num_vars = c_long()270        retcode = func(self.fh, num_vars)271        warn_or_raise(retcode, func)272        return num_vars.value273    @property274    def case_count(self) -> int:275        """Number of cases"""276        func = self.spssio.spssGetNumberofCases277        func.argtypes = [c_int, POINTER(c_long)]278        num_cases = c_long()279        retcode = func(self.fh, num_cases)280        warn_or_raise(retcode, func)...writer.py
Source:writer.py  
...41        """case_record is a string buffer"""42        func = self.spssio.spssWholeCaseOut43        func.argtypes = [c_int, c_char_p]44        retcode = func(self.fh, case_record)45        warn_or_raise(retcode, func)46    def _set_value_char(self, var_name, value):47        var_handle = self._get_var_handle(var_name.encode(self.encoding))48        func = self.spssio.spssSetValueChar49        func.argtypes = [c_int, c_double, c_char_p]50        retcode = func(self.fh, var_handle, value)51        warn_or_raise(retcode, func, var_name, value)52    def _set_value_numeric(self, var_name, value):53        var_handle = self._get_var_handle(var_name.encode(self.encoding))54        func = self.spssio.spssSetValueNumeric55        func.argtypes = [c_int, c_double, c_double]56        retcode = func(self.fh, var_handle, value)57        warn_or_raise(retcode, func, var_name, value)58    def commit_case_record(self):59        """Commit case record60        Call function after setting values with set_value61        Do not use with whole_case_out"""62        func = self.spssio.spssCommitCaseRecord63        retcode = func(self.fh)64        warn_or_raise(retcode, func)65    def write_header(self, df: DataFrame, metadata: Union[dict, SimpleNamespace] = None, **kwargs):66        """Write metadata properties67        Parameters68        ----------69        df70            DataFrame71        metadata72            Dictionary of Header attributes to use (see Header class for more detail)73        **kwargs74            Additional arguments, including individual metadata attributes.75            Note that metadata attributes supplied here take precedence.76        """77        compression = {".sav": 1, ".zsav": 2}.get(os.path.splitext(self.filename)[1].lower())78        self.compression = compression79        if metadata is None:80            metadata = {}81        elif isinstance(metadata, SimpleNamespace):82            metadata = metadata.__dict__83        # combine, with preference to kwargs84        metadata = {**metadata, **kwargs}85        metadata["var_types"] = metadata.get("var_types", {})86        metadata["var_formats"] = metadata.get("var_formats", {})87        metadata["var_measure_levels"] = metadata.get("var_measure_levels", {})88        # convert all var_formats to tuple structure89        # ignore var_formats_tuple if supplied90        # var_formats can be either plain or tuple structure91        metadata["var_formats"] = {92            var_name: varformat_to_tuple(var_format)93            for var_name, var_format in metadata["var_formats"].items()94        }95        dtypes = df.dtypes.to_dict()96        var_types = {}97        encoding = self.encoding98        # setup types, formats, levels99        for col, dtype in dtypes.items():100            if (101                metadata["var_types"].get(col)102                or is_string_dtype(dtypes.get(col))103                or is_object_dtype(dtypes.get(col))104            ):105                var_type = (106                    df[col]107                    .fillna("")108                    .apply(lambda x: (x if hasattr(x, "decode") else len(str(x).encode(encoding))))109                    .max()110                )111                var_type = max(var_type, metadata["var_types"].get(col, 1))112                var_type = min(var_type, SPSS_MAX_LONGSTRING)113                var_types[col] = var_type114                var_format = metadata["var_formats"].get(col, (1, 1, 0))115                metadata["var_formats"][col] = (116                    var_format[0] if var_format[0] in spss_string_formats else 1,117                    var_type,118                    0,119                )120            elif is_timedelta64_dtype(dtype):121                var_types[col] = 0122                metadata["var_formats"][col] = metadata["var_formats"].get(123                    col, config.default_time_format124                )125                metadata["var_measure_levels"][col] = metadata["var_measure_levels"].get(col, 3)126            elif is_datetime64_any_dtype(dtype):127                var_types[col] = 0128                metadata["var_formats"][col] = metadata["var_formats"].get(129                    col, config.default_datetime_format130                )131                metadata["var_measure_levels"][col] = metadata["var_measure_levels"].get(col, 3)132            else:133                var_types[col] = 0134                metadata["var_formats"][col] = metadata["var_formats"].get(135                    col, config.default_numeric_format136                )137                metadata["var_measure_levels"][col] = metadata["var_measure_levels"].get(col, 3)138        # initiate variables139        for col in df.columns:140            self._add_var(col, var_types[col])141        # set optional header attributes142        attr_to_ignore = [143            "case_count",144            "encoding",145            "var_names",146            "var_types",147            "var_formats_tuple",148            "var_compat_names",149        ]150        attrs = [attr for attr in dir(self) if attr[0] != "_" and attr not in attr_to_ignore]151        # catch Exceptions for non-critical attributes152        failed_to_set = {}153        for attr, v in metadata.items():154            if attr in attrs and v:155                try:156                    setattr(self, attr, v)157                except SPSSError as e:158                    failed_to_set[attr] = e159        if failed_to_set:160            warnings.warn(161                SPSSWarning(162                    "Errors occurred while writing header attributes:\n\n"163                    + "\n\n".join((f"{attr}: {error}" for attr, error in failed_to_set.items()))164                    + "\n"165                ),166                stacklevel=2,167            )168        # commit header169        self.commit_header()170    def write_data_by_val(self, df: DataFrame):171        """Write data by variable/value172        Parameters173        ----------174        df175            DataFrame176        Notes177        -----178        Slower than whole_case_out179        Use when appending to an existing data set and variable order doesn't align180        """181        pd_origin = pd.to_datetime(0)182        var_types = self.var_types183        var_handles = self.var_handles184        dtypes = df.dtypes.to_dict()185        idx_cols = dict(enumerate(df.columns))186        write_c = self.spssio.spssSetValueChar187        write_c.argtypes = [c_int, c_double, c_char_p]188        write_n = self.spssio.spssSetValueNumeric189        write_n.argtypes = [c_int, c_double, c_double]190        for row in df.itertuples(index=False, name=None):191            for idx, col in idx_cols.items():192                value = row[idx]193                if pd.notna(value):194                    # string195                    if var_types[col]:196                        value = value.encode(self.encoding)197                        retcode = write_c(self.fh, var_handles[col], value)198                        warn_or_raise(retcode, write_c, col, value)199                    # time200                    elif is_timedelta64_dtype(dtypes[col]):201                        value = value.total_seconds()202                        retcode = write_n(self.fh, var_handles[col], value)203                        warn_or_raise(retcode, write_n, col, value)204                    # datetime205                    elif is_datetime64_any_dtype(dtypes[col]):206                        value = (value - pd_origin).total_seconds() + SPSS_ORIGIN_OFFSET207                        retcode = write_n(self.fh, var_handles[col], value)208                        warn_or_raise(retcode, write_n, col, value)209                    # numeric210                    else:211                        retcode = write_n(self.fh, var_handles[col], value)212                        warn_or_raise(retcode, write_n, col, value)213            self.commit_case_record()214    def write_data(self, df: DataFrame, **kwargs):215        """Write data to file216        Parameters217        ----------218        df219            DataFrame220        """221        # basic info222        var_types = self.var_types223        sysmis = self.sysmis224        encoding = self.encoding225        pd_origin = pd.to_datetime(0)226        def get_buffer_size(var_type):...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
