messages to be sent to SMSC are queued using the said mechanism before been sent client_id: a unique string identifying a naz client class instance logger: python `logger <https://docs.python.org/3/library/logging.html#logging.Logger>`_ instance to be used for logging
- codec_class: python class instance to be used to encode/decode messages
+ codec: python class instance, that is a child class of `naz.codec.BaseCodec` to be used to encode/decode messages. enquire_link_interval: time in seconds to wait before sending an enquire_link request to SMSC to check on its status rateLimiter: python class instance implementing rate limitation hook: python class instance implemeting functionality/hooks to be called by naz \
@@ -347,7 +349,7 @@
)))
- ifnotisinstance(codec_class,(type(None),nazcodec.BaseNazCodec)):
+ ifnotisinstance(codec,(type(None),the_codec.BaseCodec)):errors.append(ValueError(
- "`codec_class` should be of type:: `None` or `naz.nazcodec.BaseNazCodec` You entered: {0}".format(
- type(codec_class)
+ "`codec` should be of type:: `None` or `naz.codec.BaseCodec` You entered: {0}".format(
+ type(codec))))
@@ -838,7 +840,8 @@
Source code for naz.client
ifnotkey.startswith("__"):ifencoding==val.code:returnval.value
- raiseValueError("That encoding:{0} is not recognised.".format(encoding))
+
+ raiseValueError("That encoding: `{0}` is not recognised.".format(encoding))def_search_by_command_id_code(self,command_id_code:int)->typing.Union[None,str]:forkey,valinself.command_ids.items():
@@ -888,15 +891,17 @@
Source code for naz.client
returns decoded string from bytes with any password removed. the returned string is safe to log. """
+ log_msg="unable to decode msg"try:
- log_msg=self.codec_class.decode(msg)
+ log_msg=self.codec.decode(msg)ifself.passwordinlog_msg:# do not log password, redact it from logs.log_msg=log_msg.replace(self.password,"{REDACTED}")
- except(UnicodeDecodeError,UnicodeError):
- log_msg=str(msg)
- exceptException:
- log_msg=""
+ except(UnicodeDecodeError,UnicodeError)ase:
+ # in future we may want to do something custom
+ _=e
+ exceptExceptionase:
+ _=ereturnlog_msg
# It may be used at a later stage to query the status of a message, cancel# or replace the message._message_id=body_data.replace(chr(0).encode(),b"")
- smsc_message_id=self.codec_class.decode(_message_id)
+ smsc_message_id=self.codec.decode(_message_id)awaitself.correlation_handler.put(smpp_command=smpp_command,sequence_number=sequence_number,
@@ -2390,7 +2416,7 @@
Source code for naz.client
_tag_value=tag_value.replace(chr(0).encode(),b"")# change variable names to make mypy happy
- t_value=self.codec_class.decode(_tag_value)
+ t_value=self.codec.decode(_tag_value)log_id,hook_metadata=awaitself.correlation_handler.get(smpp_command=smpp_command,sequence_number=sequence_number,
@@ -2426,28 +2452,6 @@
+# The code in this file is copied from https://github.com/praekelt/vumi/blob/master/vumi/codecs/vumi_codecs.py
+# which is in turn largely copied from http://stackoverflow.com/questions/13130935/decode-7-bit-gsm
+# Vumi's license is included below:
+
+# Copyright (c) Praekelt Foundation and individual contributors.
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+
+# 3. Neither the name of the Praekelt Foundation nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+
+importsys
+importabc
+importcodecs
+
+
+# An alternative to using this codec module is to use: https://github.com/dsch/gsm0338
+# however, I'm guessing that vumi has been in use longer and we should thus go with it.
+
+
+classNazCodecException(Exception):
+ pass
+
+
+classGSM7BitCodec(codecs.Codec):
+ """
+ """
+
+ gsm_basic_charset=(
+ "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\x1bÆæßÉ !\"#¤%&'()*+,-./0123456789:;"
+ "<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑÜ`¿abcdefghijklmnopqrstuvwxyzäö"
+ "ñüà"
+ )
+
+ gsm_basic_charset_map=dict((l,i)fori,linenumerate(gsm_basic_charset))
+
+ gsm_extension=(
+ "````````````````````^```````````````````{}`````\\````````````[~]`"
+ "|````````````````````````````````````€``````````````````````````"
+ )
+
+ gsm_extension_map=dict((l,i)fori,linenumerate(gsm_extension))
+
+ defencode(self,input,errors="strict"):
+ """
+ errors can be 'strict', 'replace' or 'ignore'
+ eg:
+ xcodec.encode("Zoë","gsm0338") will fail with UnicodeEncodeError
+ but
+ xcodec.encode("Zoë","gsm0338", 'replace') will return b'Zo?'
+ and
+ xcodec.encode("Zoë","gsm0338", 'ignore') will return b'Zo'
+ """
+ result=[]
+ forposition,cinenumerate(input):
+ idx=self.gsm_basic_charset_map.get(c)
+ ifidxisnotNone:
+ result.append(chr(idx))
+ continue
+ idx=self.gsm_extension_map.get(c)
+ ifidxisnotNone:
+ result.append(chr(27)+chr(idx))
+ else:
+ result.append(self.handle_encode_error(c,errors,position,input))
+
+ obj="".join(result)
+ # this is equivalent to;
+ # import six; six.b('someString')
+ # see:
+ # https://github.com/benjaminp/six/blob/68112f3193c7d4bef5ad86ed1b6ed528edd9093d/six.py#L625
+ obj=obj.encode("latin-1")
+ return(obj,len(obj))
+
+ defhandle_encode_error(self,char,handler_type,position,obj):
+ handler=getattr(self,"handle_encode_%s_error"%(handler_type,),None)
+ ifhandlerisNone:
+ raiseNazCodecException("Invalid errors type %s for GSM7BitCodec",handler_type)
+ returnhandler(char,position,obj)
+
+ @staticmethod
+ defhandle_encode_strict_error(char,position,obj):
+ raiseUnicodeEncodeError("gsm0338",char,position,position+1,repr(obj))
+
+ @staticmethod
+ defhandle_encode_ignore_error(char,position,obj):
+ return""
+
+ defhandle_encode_replace_error(self,char,position,obj):
+ returnchr(self.gsm_basic_charset_map.get("?"))
+
+ defdecode(self,input,errors="strict"):
+ """
+ errors can be 'strict', 'replace' or 'ignore'
+ """
+ res=iter(input)
+ result=[]
+ forposition,cinenumerate(res):
+ try:
+ ifc==27:
+ c=next(res)
+ result.append(self.gsm_extension[c])
+ else:
+ result.append(self.gsm_basic_charset[c])
+ exceptIndexErrorasindexErrorException:
+ result.append(
+ self.handle_decode_error(c,errors,position,input,indexErrorException)
+ )
+
+ obj="".join(result)
+ return(obj,len(obj))
+
+ defhandle_decode_error(self,char,handler_type,position,obj,indexErrorException):
+ handler=getattr(self,"handle_decode_%s_error"%(handler_type,),None)
+ ifhandlerisNone:
+ raiseNazCodecException("Invalid errors type %s for GSM7BitCodec",handler_type)
+ returnhandler(char,position,obj,indexErrorException)
+
+ @staticmethod
+ defhandle_decode_strict_error(char,position,obj,indexErrorException):
+ # https://github.com/google/pytype/issues/349
+ raiseUnicodeDecodeError(
+ "gsm0338",
+ chr(char).encode("latin-1"),
+ position,
+ position+1,
+ repr(obj),# pytype: disable=wrong-arg-types
+ )fromindexErrorException
+
+ @staticmethod
+ defhandle_decode_ignore_error(char,position,obj,indexErrorException):
+ return""
+
+ @staticmethod
+ defhandle_decode_replace_error(char,position,obj,indexErrorException):
+ return"?"
+
+
+classUCS2Codec(codecs.Codec):
+ """
+ UCS2 is for all intents & purposes assumed to be the same as
+ big endian UTF16.
+ """
+
+ defencode(self,input,errors="strict"):
+ # https://github.com/google/pytype/issues/348
+ returncodecs.utf_16_be_encode(input,errors)# pytype: disable=module-attr
+
+ defdecode(self,input,errors="strict"):
+ returncodecs.utf_16_be_decode(input,errors)# pytype: disable=module-attr
+
+
+
[docs]classBaseCodec(abc.ABC):
+ """
+ This is the interface that must be implemented to satisfy naz's encoding/decoding.
+ User implementations should inherit this class and
+ implement the :func:`__init__ <BaseCodec.__init__>`, :func:`encode <BaseCodec.encode>` and :func:`decode <BaseCodec.decode>`
+ methods with the type signatures shown.
+
+ naz calls an implementation of this class to encode/decode messages.
+ """
+
+
[docs]@abc.abstractmethod
+ def__init__(self,encoding:str,errors:str)->None:
+ """
+ Parameters:
+ encoding: `encoding <https://docs.python.org/3/library/codecs.html#standard-encodings>`_ used to encode messages been sent to SMSC
+ The encoding should be one of the encodings recognised by the SMPP specification. See section 5.2.19 of SMPP spec
+ eg gsm0338, ucs2 etc
+ errors: same meaning as the errors argument to pythons' `encode <https://docs.python.org/3/library/codecs.html#codecs.encode>`_ method
+ """
+ self.encoding=encoding
+ self.errors=errors
+
+
[docs]@abc.abstractmethod
+ defencode(self,input:str)->bytes:
+ """
+ return an encoded version of the string as a bytes object
+
+ Parameters:
+ input: the string to encode
+ """
+ raiseNotImplementedError("encode method must be implemented.")
+
+
[docs]defdecode(self,input:bytes)->str:
+ """
+ return a string decoded from the given bytes.
+
+ Parameters:
+ input: the bytes to decode
+ """
+ raiseNotImplementedError("decode method must be implemented.")
+
+
+
[docs]classSimpleCodec(BaseCodec):
+ """
+ This is an implementation of `BaseCodec`
+
+ SMPP uses a 7-bit GSM character set. This class implements that encoding/decoding scheme.
+ This class can also be used with the usual `python standard encodings <https://docs.python.org/3/library/codecs.html#standard-encodings>`_
+
+ example usage:
+
+ .. highlight:: python
+ .. code-block:: python
+
+ ncodec = SimpleCodec(encoding="ucs2")
+
+ ncodec.encode("Zoë")
+ ncodec.decode(b'Zo\xc3\xab')
+ """
+
+ custom_codecs={"gsm0338":GSM7BitCodec(),"ucs2":UCS2Codec()}
+
+
[docs]def__init__(self,encoding:str="gsm0338",errors:str="strict")->None:
+ """
+ Parameters:
+ encoding: `encoding <https://docs.python.org/3/library/codecs.html#standard-encodings>`_ used to encode messages been sent to SMSC
+ The encoding should be one of the encodings recognised by the SMPP specification. See section 5.2.19 of SMPP spec
+ errors: same meaning as the errors argument to pythons' `encode <https://docs.python.org/3/library/codecs.html#codecs.encode>`_ method
+ """
+ ifnotisinstance(encoding,str):
+ raiseValueError(
+ "`encoding` should be of type:: `str` You entered: {0}".format(type(encoding))
+ )
+ ifnotisinstance(errors,str):
+ raiseValueError(
+ "`errors` should be of type:: `str` You entered: {0}".format(type(errors))
+ )
+ self.encoding=encoding
+ self.errors=errors
[docs]classSimpleLogger(logging.Logger):"""
- It implements a structured logger that renders logs as json.
+ It implements a structured logger that renders logs as either json(default) or python dictionary. example usage:
@@ -191,6 +191,7 @@
log_metadata: metadata that will be included in all log statements handler: python logging `handler <https://docs.python.org/3/library/logging.html#logging.Handler>`_ to be attached to this logger. By default, `logging.StreamHandler` is used.
+ render_as_json: whether to render logs as json or dictionary. By default it renders as json. """super(SimpleLogger,self).__init__(name=logger_name,level=self._nameToLevel(level))ifnotisinstance(logger_name,str):
@@ -228,6 +230,12 @@
Source code for naz.log
type(handler)))
+ ifnotisinstance(render_as_json,bool):
+ raiseValueError(
+ "`render_as_json` should be of type:: `bool` You entered: {0}".format(
+ type(render_as_json)
+ )
+ )self.logger_name=logger_nameself.level=self._nameToLevel(level)iflog_metadataisnotNone:
@@ -240,6 +248,7 @@
"`level` should be one of; 'NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'")frome
- def_process_msg(self,msg:typing.Union[str,dict])->str:
+ def_process_msg(self,msg:typing.Union[str,dict])->typing.Union[str,dict]:timestamp=self._formatTime()ifisinstance(msg,dict):_timestamp={"timestamp":timestamp}# _timestamp should appear first in resulting dictdict_merged_msg={**_timestamp,**msg,**self.log_metadata}
- returnself._to_json(dict_merged_msg)
+ ifself.render_as_json:
+ returnself._to_json(dict_merged_msg)
+ else:
+ returndict_merged_msgelse:str_merged_msg="{0}{1}{2}".format(timestamp,msg,self.log_metadata)ifself.log_metadata=={}:str_merged_msg="{0}{1}".format(timestamp,msg)
- returnself._to_json(str_merged_msg)
+ ifself.render_as_json:
+ returnself._to_json(str_merged_msg)
+ else:
+ returnstr_merged_msgdef_formatTime(self)->str:"""
@@ -403,11 +418,12 @@
Source code for naz.log
target=target,flushOnClose=flushOnClose,# pytype: disable=wrong-keyword-args)
- self.buffer:collections.deque=collections.deque(
- maxlen=self.capacity# type: ignore
- )# pytype: disable=attribute-error
+
# assuming each log record is 250 bytes, then the maximum
- # memory used by `buffer` will always be == 250*10_000/(1000*1000) == 2.5MB
+ # memory used by `buffer` will always be == 250*1_000/(1000*1000) == 0.25MB
+ self.buffer:typing.Deque[logging.LogRecord]=collections.deque(# type: ignore
+ maxlen=self.capacity# pytype: disable=attribute-error
+ )self.heartbeatInterval=heartbeatIntervalifself.heartbeatInterval:
diff --git a/docs/_modules/naz/nazcodec.html b/docs/_modules/naz/nazcodec.html
index 4347688a..ee05dc3e 100644
--- a/docs/_modules/naz/nazcodec.html
+++ b/docs/_modules/naz/nazcodec.html
@@ -8,7 +8,7 @@
- naz.nazcodec — naz v0.7.4 documentation
+ naz.nazcodec — naz v0.7.5 documentation
@@ -60,7 +60,7 @@
- v0.7.4
+ v0.7.5
@@ -196,9 +196,10 @@
Source code for naz.nazcodec
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE# POSSIBILITY OF SUCH DAMAGE.
-importabc
+
importsysimportcodecs
+importtyping# An alternative to using this codec module is to use: https://github.com/dsch/gsm0338
@@ -336,65 +337,9 @@
[docs]classBaseNazCodec(abc.ABC):
- """
- This is the interface that must be implemented to satisfy naz's encoding/decoding.
- User implementations should inherit this class and
- implement the :func:`encode <BaseNazCodec.encode>` and :func:`decode <BaseNazCodec.decode>` methods with the type signatures shown.
-
- naz calls an implementation of this class to encode/decode messages.
- """
-
-
[docs]def__init__(self,encoding:str="gsm0338",errors_level:str="strict")->None:
- """
- Parameters:
- encoding: `encoding <https://docs.python.org/3/library/codecs.html#standard-encodings>`_ used to encode messages been sent to SMSC
- errors_level: same meaning as the errors argument to pythons' `encode <https://docs.python.org/3/library/codecs.html#codecs.encode>`_ method
- """
- ifnotisinstance(encoding,str):
- raiseValueError(
- "`encoding` should be of type:: `str` You entered: {0}".format(type(encoding))
- )
- ifnotisinstance(errors_level,str):
- raiseValueError(
- "`errors_level` should be of type:: `str` You entered: {0}".format(
- type(errors_level)
- )
- )
- self.encoding=encoding
- self.errors_level=errors_level
-
-
[docs]@abc.abstractmethod
- defencode(self,string_to_encode:str)->bytes:
- """
- return an encoded version of the string as a bytes object
-
- Parameters:
- string_to_encode: the string to encode
- encoding: encoding scheme. eg utf-8, gsm0338 etc
- errors: `same as defined in pythons codec.encode <https://docs.python.org/3/library/codecs.html#codecs.encode>`_
-
- Returns:
- encoded version of input string
- """
- raiseNotImplementedError("encode method must be implemented.")
-
-
[docs]@abc.abstractmethod
- defdecode(self,byte_string:bytes)->str:
- """
- return a string decoded from the given bytes.
-
- Parameters:
- byte_string: the bytes to decode
- encoding: encoding scheme. eg utf-8, gsm0338 etc
- errors: `same as defined in pythons codec.decode <https://docs.python.org/3/library/codecs.html#codecs.decode>`_
- """
- raiseNotImplementedError("decode method must be implemented.")
[docs]classSimpleCodec(codecs.Codec):"""
- This is an implementation of BaseNazCodec.
+ This is an implementation of `codecs.Codec <https://docs.python.org/3/library/codecs.html>`_ SMPP uses a 7-bit GSM character set. This class implements that encoding/decoding scheme. This class can also be used with the usual `python standard encodings <https://docs.python.org/3/library/codecs.html#standard-encodings>`_
@@ -404,54 +349,45 @@
[docs]def__init__(self,encoding:str="gsm0338")->None:""" Parameters:
- encoding: `encoding <https://docs.python.org/3/library/codecs.html#standard-encodings>`_ used to encode messages been sent to SMSC
- errors_level: same meaning as the errors argument to pythons' `encode <https://docs.python.org/3/library/codecs.html#codecs.encode>`_ method
+ encoding: `encoding <https://docs.python.org/3/library/codecs.html#standard-encodings>`_ used to encode messages been sent to SMSC
+ The encoding should be one of the encodings recognised by the SMPP specification. See section 5.2.19 of SMPP spec """ifnotisinstance(encoding,str):raiseValueError("`encoding` should be of type:: `str` You entered: {0}".format(type(encoding)))
- ifnotisinstance(errors_level,str):
- raiseValueError(
- "`errors_level` should be of type:: `str` You entered: {0}".format(
- type(errors_level)
- )
- )
- self.encoding=encoding
- self.errors_level=errors_level
smpp_command: any one of the SMSC commands eg submit_sm log_id: a unique identify of this reque pdu: the full PDU as sent to SMSC. It is mutually exclusive with `short_message`.
- codec_class: python class instance to be used to encode/decode messages. It should be a child class of `naz.nazcodec.BaseNazCodec`.
+ codec: python class instance to be used to encode/decode messages. It should be a child class of `naz.codec.BaseCodec`. You should only specify this, if you also specified `pdu`, else you can leave it as None. short_message: message to send to SMSC. It is mutually exclusive with `pdu` source_addr: the identifier(eg msisdn) of the message sender.
@@ -222,7 +222,7 @@
type(hook_metadata)))
- ifnotisinstance(codec_class,(type(None),nazcodec.BaseNazCodec)):
+ ifnotisinstance(codec,(type(None),the_codec.BaseCodec)):raiseValueError(
- "`codec_class` should be of type:: `None` or `naz.nazcodec.BaseNazCodec` You entered: {0}".format(
- type(codec_class)
+ "`codec` should be of type:: `None` or `naz.codec.BaseCodec` You entered: {0}".format(
+ type(codec)))
- ifpduandnotcodec_class:
+ ifpduandnotcodec:# because pdu is in bytes, when converting to string; we need to use whatever encoding was passed in
- # thus one has to supply the codec_class
- raiseValueError("You cannot specify `pdu` and not a `codec_class`")
+ # thus one has to supply the codec
+ raiseValueError("You cannot specify `pdu` and not a `codec`")
[docs]@staticmethoddeffrom_json(
- json_message:str,codec_class:typing.Union[None,nazcodec.BaseNazCodec]=None
+ json_message:str,codec:typing.Union[None,the_codec.BaseCodec]=None)->Message:""" Deserializes the message protocol from json. You can use this method if you would
@@ -338,22 +338,22 @@
Source code for naz.protocol
Parameters: json_message: `naz.protocol.Message` in json format.
- codec_class: python class instance to be used to encode/decode messages. It should be a child class of `naz.nazcodec.BaseNazCodec`.
+ codec: python class instance to be used to encode/decode messages. It should be a child class of `naz.codec.BaseCodec`. You should only specify this, if `json_message` has a key called `pdu` and it is not None. """_in_dict=json.loads(json_message)if_in_dict.get("pdu"):
- ifnotcodec_class:
+ ifnotcodec:# because pdu is in bytes, when converting to string; we need to use whatever encoding was passed in
- # thus one has to supply the codec_class
- raiseValueError("You cannot specify `pdu` and not a `codec_class`")
+ # thus one has to supply the codec
+ raiseValueError("You cannot specify `pdu` and not a `codec`")# we need to convert pdu to bytes.
- # There's a risk of a message been encoded with an codec_class that it was not decoded with when been saved to broker
- # ideally, the codec_class should also be saved/serialized as part of the message been sent to broker.
+ # There's a risk of a message been encoded with an codec that it was not decoded with when been saved to broker
+ # ideally, the codec should also be saved/serialized as part of the message been sent to broker.# However, this risk is small and we should only consider it if people report it as a bug.
- _in_dict["pdu"]=codec_class.encode(_in_dict["pdu"])
- _in_dict["codec_class"]=codec_class
+ _in_dict["pdu"]=codec.encode(_in_dict["pdu"])
+ _in_dict["codec"]=codecreturnMessage(**_in_dict)
broker (BaseBroker) – python class instance implementing some queueing mechanism. messages to be sent to SMSC are queued using the said mechanism before been sent
client_id (Union[None, str]) – a unique string identifying a naz client class instance
logger (Union[None, Logger]) – python logger instance to be used for logging
-
codec_class (Union[None, BaseNazCodec]) – python class instance to be used to encode/decode messages
+
codec (Union[None, BaseCodec]) – python class instance, that is a child class of naz.codec.BaseCodec to be used to encode/decode messages.
enquire_link_interval (float) – time in seconds to wait before sending an enquire_link request to SMSC to check on its status
rateLimiter (Union[None, BaseRateLimiter]) – python class instance implementing rate limitation
hook (Union[None, BaseHook]) – python class instance implemeting functionality/hooks to be called by naz just before sending request to SMSC and just after getting response from SMSC
This is the interface that must be implemented to satisfy naz’s encoding/decoding.
+User implementations should inherit this class and
+implement the __init__, encode and decode
+methods with the type signatures shown.
+
naz calls an implementation of this class to encode/decode messages.
encoding (str) – encoding used to encode messages been sent to SMSC
+The encoding should be one of the encodings recognised by the SMPP specification. See section 5.2.19 of SMPP spec
+eg gsm0338, ucs2 etc
+
errors (str) – same meaning as the errors argument to pythons’ encode method
SMPP uses a 7-bit GSM character set. This class implements that encoding/decoding scheme.
+This class can also be used with the usual python standard encodings
encoding used to encode messages been sent to SMSC
+The encoding should be one of the encodings recognised by the SMPP specification. See section 5.2.19 of SMPP spec
+
+
errors (str) –
same meaning as the errors argument to pythons’ encode method