Connect to Mambu, make the request to the REST API.

        If there's no urlfunc to use, nothing is done here.

        When done, initializes the attrs attribute of the Mambu object
        by calling the init method. Please refer to that code and pydoc
        for further information.

        Uses urllib module to connect. Since all Mambu REST API
        responses are json, uses json module to translate the response
        to a valid python object (dictionary or list).

        When Mambu returns a response with returnCode and returnStatus
        fields, it means something went wrong with the request, and a
        MambuError is thrown detailing the error given by Mambu itself.

        If you need to make a POST request, send a data argument to the
        new Mambu object.

        Provides to prevent errors due to using special chars on the
        request URL. See mambuutil.iriToUri() method pydoc for further
        info.

        Provides to prevent errors due to using special chars on the
        parameters of a POST request. See mambuutil.encoded_dict()
        method pydoc for further info.

        For every request done, the request counter Singleton is
        increased.

        Includes retry logic, to provide for a max number of connection
        failures with Mambu. If maximum retries are reached,
        MambuCommError is thrown.

        Includes pagination code. Mambu supports a max of 500 elements
        per response. Such an ugly detail is wrapped here so further
        pagination logic is not needed above here. You need a million
        elements? you got them by making several 500 elements requests
        later joined together in a sigle list. Just send a limit=0
        (default) and that's it.

.. todo:: improve raised exception messages. Sometimes
          MambuCommErrors are thrown due to reasons not always
          clear when catched down the road, but that perhaps may
          be noticed in here and aren't fully reported to the
          user. Specially serious on retries-MambuCommError
          situations (the except Exception that increases the
          retries counter is not saving the exception message,
          just retrying).

.. todo:: what about using decorators for the retry and for the
          window logic?
          (https://www.oreilly.com/ideas/5-reasons-you-need-to-learn-to-write-python-decorators
          # Reusing impossible-to-reuse code)

def connect(self, *args, **kwargs):
        """Connect to Mambu, make the request to the REST API.

        If there's no urlfunc to use, nothing is done here.

        When done, initializes the attrs attribute of the Mambu object
        by calling the init method. Please refer to that code and pydoc
        for further information.

        Uses urllib module to connect. Since all Mambu REST API
        responses are json, uses json module to translate the response
        to a valid python object (dictionary or list).

        When Mambu returns a response with returnCode and returnStatus
        fields, it means something went wrong with the request, and a
        MambuError is thrown detailing the error given by Mambu itself.

        If you need to make a POST request, send a data argument to the
        new Mambu object.

        Provides to prevent errors due to using special chars on the
        request URL. See mambuutil.iriToUri() method pydoc for further
        info.

        Provides to prevent errors due to using special chars on the
        parameters of a POST request. See mambuutil.encoded_dict()
        method pydoc for further info.

        For every request done, the request counter Singleton is
        increased.

        Includes retry logic, to provide for a max number of connection
        failures with Mambu. If maximum retries are reached,
        MambuCommError is thrown.

        Includes pagination code. Mambu supports a max of 500 elements
        per response. Such an ugly detail is wrapped here so further
        pagination logic is not needed above here. You need a million
        elements? you got them by making several 500 elements requests
        later joined together in a sigle list. Just send a limit=0
        (default) and that's it.

.. todo:: improve raised exception messages. Sometimes
          MambuCommErrors are thrown due to reasons not always
          clear when catched down the road, but that perhaps may
          be noticed in here and aren't fully reported to the
          user. Specially serious on retries-MambuCommError
          situations (the except Exception that increases the
          retries counter is not saving the exception message,
          just retrying).

.. todo:: what about using decorators for the retry and for the
          window logic?
          (https://www.oreilly.com/ideas/5-reasons-you-need-to-learn-to-write-python-decorators
          # Reusing impossible-to-reuse code)
        """
        from copy import deepcopy
        if args:
            self.__args = deepcopy(args)
        if kwargs:
            for k,v in kwargs.items():
                self.__kwargs[k] = deepcopy(v)

        jsresp = {}

        if not self.__urlfunc:
            return

        # Pagination window, Mambu restricts at most 500 elements in response
        offset = self.__offset
        window = True
        jsresp = {}
        while window:
            if not self.__limit or self.__limit > OUT_OF_BOUNDS_PAGINATION_LIMIT_VALUE:
                limit = OUT_OF_BOUNDS_PAGINATION_LIMIT_VALUE
            else:
                limit = self.__limit

            # Retry mechanism, for awful connections
            retries = 0
            while retries < MambuStruct.RETRIES:
                try:
                    # Basic authentication
                    user = self.__kwargs.get('user', apiuser)
                    pwd = self.__kwargs.get('pwd', apipwd)
                    if self.__data:
                        headers = {'content-type': 'application/json'}
                        data = json.dumps(encoded_dict(self.__data))
                        url = iriToUri(self.__urlfunc(self.entid, limit=limit, offset=offset, *self.__args, **self.__kwargs))
                    # PATCH
                        if self.__method=="PATCH":
                            resp = requests.patch(url, data=data, headers=headers, auth=(user, pwd))
                    # POST
                        else:
                            resp = requests.post(url, data=data, headers=headers, auth=(user, pwd))
                    # GET
                    else:
                        url = iriToUri(self.__urlfunc(self.entid, limit=limit, offset=offset, *self.__args, **self.__kwargs))
                        resp = requests.get(url, auth=(user, pwd))
                    # Always count a new request when done!
                    self.rc.add(datetime.now())
                    try:
                        jsonresp = json.loads(resp.content)
                        # Returns list: extend list for offset
                        if type(jsonresp) == list:
                            try:
                                jsresp.extend(jsonresp)
                            except AttributeError:
                                # First window, forget that jsresp was a dict, turn it in to a list
                                jsresp=jsonresp
                            if len(jsonresp) < limit:
                                window = False
                        # Returns dict: in theory Mambu REST API doesn't takes limit/offset in to account
                        else:
                            jsresp = jsonresp
                            window = False
                    except ValueError as ex:
                        # json.loads invalid data argument
                        raise ex
                    except Exception as ex:
                        # any other json error
                        raise MambuError("JSON Error: %s" % repr(ex))
                    # if we reach here, we're done and safe
                    break
                except MambuError as merr:
                    raise merr
                except requests.exceptions.RequestException:
                    retries += 1
                except Exception as ex:
                    raise ex
            else:
                raise MambuCommError("ERROR I can't communicate with Mambu")

            # next window, moving offset...
            offset = offset + limit
            if self.__limit:
                self.__limit -= limit
                if self.__limit <= 0:
                    window = False
                    self.__limit = self.__inilimit

        try:
            if u'returnCode' in jsresp and u'returnStatus' in jsresp and jsresp[u'returnCode'] != 0:
                raise MambuError(jsresp[u'returnStatus'])
        except AttributeError:
            pass

        if self.__method != "PATCH":
            self.init(attrs=jsresp, *self.__args, **self.__kwargs)