From 7fc36f248829e7b6b7f0308570823f2bf9db4b46 Mon Sep 17 00:00:00 2001 From: Pavel Perestoronin Date: Tue, 2 Oct 2018 12:00:58 +0200 Subject: [PATCH] Improve PEP-compliance and testing * Add `tox.ini`, support `2.6`, `2.7`, `3.4`, `3.5`, `3.6` and `3.7` * Drop Python `3.2` and `3.3` support * Improve PEP8-compliance in a few places * Bump version to `1.0.2` and mark it as stable * Add classifiers to `setup.py` * Improve `.gitignore` with standard templates for popular environments * Remove some dead code in comments --- .gitignore | 319 ++++++++++++++++++++++++++++++++++++--- CHANGELOG.md | 9 ++ setup.py | 44 +++--- smpplib/__init__.py | 6 +- smpplib/client.py | 117 +++++++------- smpplib/command.py | 150 +++++++----------- smpplib/command_codes.py | 23 +-- smpplib/consts.py | 209 ++++++++++++------------- smpplib/gsm.py | 3 +- smpplib/pdu.py | 9 +- smpplib/smpp.py | 3 +- tests/test_gsm.py | 6 +- tox.ini | 8 + 13 files changed, 578 insertions(+), 328 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index d2d6f36..7abb8ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,318 @@ + +# Created by https://www.gitignore.io/api/linux,macos,python,windows,virtualenv,sublimetext,intellij+all + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ *.py[cod] +*$py.class # C extensions *.so -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ .installed.cfg -lib -lib64 +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec # Installer logs pip-log.txt +pip-delete-this-directory.txt # Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ .coverage -.tox +.coverage.* +.cache nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ # Translations *.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +### Python Patch ### +.venv/ + +### Python.VirtualEnv Stack ### +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +pip-selfcheck.json + +### SublimeText ### +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +### VirtualEnv ### +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + -# Mr Developer -.mr.developer.cfg -.project -.pydevproject +# End of https://www.gitignore.io/api/linux,macos,python,windows,virtualenv,sublimetext,intellij+all diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8b0728d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +### 1.0.2 + +* Add `tox.ini`, support `2.6`, `2.7`, `3.4`, `3.5`, `3.6` and `3.7` +* Drop Python `3.2` and `3.3` support +* Improve PEP8-compliance in a few places +* Bump version to `1.0.2` and mark it as stable +* Add classifiers to `setup.py` +* Improve `.gitignore` with standard templates for popular environments +* Remove some dead code in comments diff --git a/setup.py b/setup.py index 3f3540d..210a03d 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,33 @@ -from setuptools import setup, find_packages -import sys +from setuptools import find_packages, setup -setup(name="python-smpplib", - version='1.0.1', - url='https://github.com/podshumok/python-smpplib', - description='SMPP library for python', - packages=find_packages(), - install_requires=['six'], - tests_require=['pytest', 'mock'], - zip_safe=True, - classifiers=[ - 'Development Status :: 4 - Beta', +setup( + name='python-smpplib', + version='1.0.2', + url='https://github.com/podshumok/python-smpplib', + description='SMPP library for python', + long_description=open('README.md', 'rt').read(), + long_description_content_type='text/markdown', + packages=find_packages(), + install_requires=['six'], + tests_require=['pytest', 'mock', 'tox'], + zip_safe=True, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Telecommunications Industry', + 'License :: OSI Approved', 'Operating System :: OS Independent', - 'Programming Language :: Python', + 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.1', - 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python', 'Topic :: Communications :: Telephony', - 'Intended Audience :: Telecommunications Industry', - 'License :: OSI Approved', - ], + 'Topic :: Communications', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Software Development :: Libraries', + ], ) diff --git a/smpplib/__init__.py b/smpplib/__init__.py index 27b6520..4f757be 100644 --- a/smpplib/__init__.py +++ b/smpplib/__init__.py @@ -20,8 +20,4 @@ # Modified by Yusuf Kaka # Added support for Optional TLV's -from . import smpp -from . import pdu -from . import command -from . import client -from . import exceptions +from smpplib import client, command, exceptions, pdu, smpp diff --git a/smpplib/client.py b/smpplib/client.py index 0eb58af..2b0f4d4 100644 --- a/smpplib/client.py +++ b/smpplib/client.py @@ -22,18 +22,17 @@ """SMPP client module""" -import socket -import select -import struct import binascii import logging +import select +import socket +import struct -from . import smpp -from . import exceptions -from . import consts +from smpplib import consts, exceptions, smpp logger = logging.getLogger('smpplib.client') + class SimpleSequenceGenerator(object): MIN_SEQUENCE = 0x00000001 @@ -53,6 +52,7 @@ def next_sequence(self): self._sequence += 1 return self._sequence + class Client(object): """SMPP client class""" @@ -65,8 +65,6 @@ class Client(object): sequence_generator = None def __init__(self, host, port, timeout=5, sequence_generator=None): - """Initialize""" - self.host = host self.port = int(port) self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -122,7 +120,6 @@ def _bind(self, command_name, **kwargs): if command_name in ('bind_receiver', 'bind_transceiver'): logger.debug('Receiver mode') - #smppinst = smpp.get_instance() p = smpp.make_pdu(command_name, client=self, **kwargs) self.send_pdu(p) @@ -131,9 +128,12 @@ def _bind(self, command_name, **kwargs): except socket.timeout: raise exceptions.ConnectionError() if resp.is_error(): - raise exceptions.PDUError( - '({}) {}: {}'.format(resp.status, resp.command, - consts.DESCRIPTIONS.get(resp.status, 'Unknown code')), int(resp.status)) + raise exceptions.PDUError('({}) {}: {}'.format( + resp.status, + resp.command, + consts.DESCRIPTIONS.get(resp.status, 'Unknown code')), + int(resp.status), + ) return resp def bind_transmitter(self, **kwargs): @@ -162,21 +162,21 @@ def unbind(self): def send_pdu(self, p): """Send PDU to the SMSC""" - if not self.state in consts.COMMAND_STATES[p.command]: - raise exceptions.PDUError("Command %s failed: %s" % - (p.command, consts.DESCRIPTIONS[consts.SMPP_ESME_RINVBNDSTS])) + if self.state not in consts.COMMAND_STATES[p.command]: + raise exceptions.PDUError("Command %s failed: %s" % ( + p.command, + consts.DESCRIPTIONS[consts.SMPP_ESME_RINVBNDSTS], + )) logger.debug('Sending %s PDU', p.command) generated = p.generate() - logger.debug('>>%s (%d bytes)', binascii.b2a_hex(generated), - len(generated)) + logger.debug('>>%s (%d bytes)', binascii.b2a_hex(generated), len(generated)) sent = 0 while sent < len(generated): - sent_last = 0 try: sent_last = self._socket.send(generated[sent:]) except socket.error as e: @@ -214,42 +214,40 @@ def read_pdu(self): logger.debug('<<%s (%d bytes)', binascii.b2a_hex(raw_pdu), len(raw_pdu)) - p = smpp.parse_pdu(raw_pdu, client=self) + pdu = smpp.parse_pdu(raw_pdu, client=self) - logger.debug('Read %s PDU', p.command) + logger.debug('Read %s PDU', pdu.command) - if p.is_error(): - return p + if pdu.is_error(): + return pdu - elif p.command in consts.STATE_SETTERS: - self.state = consts.STATE_SETTERS[p.command] + elif pdu.command in consts.STATE_SETTERS: + self.state = consts.STATE_SETTERS[pdu.command] - return p + return pdu def accept(self, obj): """Accept an object""" raise NotImplementedError('not implemented') - def _message_received(self, p): + def _message_received(self, pdu): """Handler for received message event""" - status = self.message_received_handler(pdu=p) + status = self.message_received_handler(pdu=pdu) if status is None: status = consts.SMPP_ESME_ROK dsmr = smpp.make_pdu('deliver_sm_resp', client=self, status=status) - #, message_id=args['pdu'].sm_default_msg_id) - dsmr.sequence = p.sequence + dsmr.sequence = pdu.sequence self.send_pdu(dsmr) def _enquire_link_received(self): """Response to enquire_link""" ler = smpp.make_pdu('enquire_link_resp', client=self) - #, message_id=args['pdu'].sm_default_msg_id) self.send_pdu(ler) logger.debug("Link Enquiry...") - def _alert_notification(self, p): - """Handler for alert notifiction event""" - self.message_received_handler(pdu=p) + def _alert_notification(self, pdu): + """Handler for alert notification event""" + self.message_received_handler(pdu=pdu) def set_message_received_handler(self, func): """Set new function to handle message receive event""" @@ -267,53 +265,54 @@ def message_received_handler(pdu, **kwargs): @staticmethod def message_sent_handler(pdu, **kwargs): - """Called when SMPP server accept message (SUBMIT_SM_RESP). - May be overridden""" + """ + Called when SMPP server accept message (SUBMIT_SM_RESP). + May be overridden + """ logger.warning('Message sent handler (Override me)') - def read_once(self, ignore_error_codes=None): """Read a PDU and act""" try: try: - p = self.read_pdu() + pdu = self.read_pdu() except socket.timeout: logger.debug('Socket timeout, listening again') - p = smpp.make_pdu('enquire_link', client=self) - self.send_pdu(p) + pdu = smpp.make_pdu('enquire_link', client=self) + self.send_pdu(pdu) return - if p.is_error(): - raise exceptions.PDUError( - '({}) {}: {}'.format(p.status, p.command, - consts.DESCRIPTIONS.get(p.status, 'Unknown status')), int(p.status)) + if pdu.is_error(): + raise exceptions.PDUError('({}) {}: {}'.format( + pdu.status, + pdu.command, + consts.DESCRIPTIONS.get(pdu.status, 'Unknown status')), + int(pdu.status), + ) - if p.command == 'unbind': # unbind_res + if pdu.command == 'unbind': # unbind_res logger.info('Unbind command received') return - elif p.command == 'submit_sm_resp': - self.message_sent_handler(pdu=p) - elif p.command == 'deliver_sm': - self._message_received(p) - elif p.command == 'enquire_link': + elif pdu.command == 'submit_sm_resp': + self.message_sent_handler(pdu=pdu) + elif pdu.command == 'deliver_sm': + self._message_received(pdu) + elif pdu.command == 'enquire_link': self._enquire_link_received() - elif p.command == 'enquire_link_resp': + elif pdu.command == 'enquire_link_resp': pass - elif p.command == 'alert_notification': - self._alert_notification(p) + elif pdu.command == 'alert_notification': + self._alert_notification(pdu) else: - logger.warning('Unhandled SMPP command "%s"', p.command) + logger.warning('Unhandled SMPP command "%s"', pdu.command) except exceptions.PDUError as e: - if ignore_error_codes \ - and len(e.args) > 1 \ - and e.args[1] in ignore_error_codes: - logging.warning('(%d) %s. Ignored.' % - (e.args[1], e.args[0])) + if ignore_error_codes and len(e.args) > 1 and e.args[1] in ignore_error_codes: + logging.warning('(%d) %s. Ignored.', e.args[1], e.args[0]) else: raise def poll(self, ignore_error_codes=None): - '''Act on available PDUs and return''' + """Act on available PDUs and return""" while True: readable, writable, exceptional = select.select([self._socket], [], [], 0) if not readable: diff --git a/smpplib/command.py b/smpplib/command.py index ef03c71..d13c6cd 100644 --- a/smpplib/command.py +++ b/smpplib/command.py @@ -22,14 +22,13 @@ """SMPP Commands module""" -import struct import logging +import struct + import six -from . import pdu -from . import exceptions -from . import consts -from .ptypes import ostr, flag +from smpplib import consts, exceptions, pdu +from smpplib.ptypes import flag, ostr logger = logging.getLogger('smpplib.command') @@ -59,8 +58,7 @@ def factory(command_name, **kwargs): 'alert_notification': AlertNotification, }[command_name](command_name, **kwargs) except KeyError: - raise exceptions.UnknownCommandError( - 'Command "%s" is not supported' % command_name) + raise exceptions.UnknownCommandError('Command "%s" is not supported' % command_name) def get_optional_name(code): @@ -71,8 +69,7 @@ def get_optional_name(code): if value == code: return key - raise exceptions.UnknownCommandError( - 'Unknown SMPP command code "0x%x"' % code) + raise exceptions.UnknownCommandError('Unknown SMPP command code "0x%x"' % code) def get_optional_code(name): @@ -82,8 +79,7 @@ def get_optional_code(name): try: return consts.OPTIONAL_PARAMS[name] except KeyError: - raise exceptions.UnknownCommandError( - 'Unknown SMPP command name "%s"' % name) + raise exceptions.UnknownCommandError('Unknown SMPP command name "%s"' % name) def unpack_short(data, pos): @@ -96,8 +92,6 @@ class Command(pdu.PDU): params = {} def __init__(self, command, need_sequence=True, **kwargs): - """Initialize""" - super(Command, self).__init__(**kwargs) self.command = command @@ -107,10 +101,6 @@ def __init__(self, command, need_sequence=True, **kwargs): if kwargs.get('status') is None: self.status = consts.SMPP_ESME_ROK - #if self.is_vendor() and self.vdefs: - # self.defs = self.defs + self.vdefs - - #self.__dict__.update(**(args)) self._set_vars(**kwargs) def _set_vars(self, **kwargs): @@ -128,9 +118,7 @@ def generate_params(self): body = consts.EMPTY_STRING for field in self.params_order: - #print field param = self.params[field] - #print param if self.field_is_optional(field): if param.type is int: value = self._generate_int_tlv(field) @@ -155,7 +143,6 @@ def generate_params(self): value = self._generate_ostring(field) if value: body += value - #print value return body def _generate_opt_header(self, field): @@ -211,7 +198,6 @@ def _generate_int_tlv(self, field): value = None if data: value = struct.pack(">HH" + fmt, field_code, field_length, data) - #print binascii.b2a_hex(value) return value def _generate_string_tlv(self, field): @@ -232,10 +218,8 @@ def _generate_string_tlv(self, field): field_length = len(field_value) fvalue = field_value + chr(0) value = struct.pack(">HH", field_code, field_length) + fvalue - #print binascii.b2a_hex(value) else: value = None # chr(0) - #setattr(self, field, field_value) return value def _generate_ostring_tlv(self, field): @@ -250,7 +234,6 @@ def _generate_ostring_tlv(self, field): if field_value: field_length = len(field_value) value = struct.pack(">HH", field_code, field_length) + field_value - #print binascii.b2a_hex(value) return value def _pack_format(self, field): @@ -265,8 +248,10 @@ def _pack_format(self, field): return None def _parse_int(self, field, data, pos): - """Parse fixed-length chunk from a PDU. - Return (data, pos) tuple.""" + """ + Parse fixed-length chunk from a PDU. + Return (data, pos) tuple. + """ size = self.params[field].size fmt = self._pack_format(field) @@ -278,8 +263,10 @@ def _parse_int(self, field, data, pos): return data, pos def _parse_string(self, field, data, pos): - """Parse variable-length string from a PDU. - Return (data, pos) tuple.""" + """ + Parse variable-length string from a PDU. + Return (data, pos) tuple. + """ end = data.find(consts.NULL_STRING, pos) length = end - pos @@ -291,13 +278,14 @@ def _parse_string(self, field, data, pos): return data, pos def _parse_ostring(self, field, data, pos, length=None): - """Parse an octet string from a PDU. - Return (data, pos) tuple.""" + """ + Parse an octet string from a PDU. + Return (data, pos) tuple. + """ if length is None: length_field = self.params[field].len_field length = int(getattr(self, length_field)) - #print length_field, type(length_field), length, type(length_field) setattr(self, field, data[pos:pos + length]) pos += length @@ -328,9 +316,7 @@ def parse_params(self, data): data, pos = self._parse_string(field, data, pos) elif param.type is ostr: data, pos = self._parse_ostring(field, data, pos) - #print pos,field,data if pos < dlen: - #None self.parse_optional_params(data[pos:]) def parse_optional_params(self, data): @@ -375,14 +361,11 @@ class Param(object): """Command parameter info class""" def __init__(self, **kwargs): - """Initialize""" - if 'type' not in kwargs: raise KeyError('Parameter Type not defined') if kwargs.get('type') not in (int, str, ostr, flag): - raise ValueError("Invalid parameter type: %s" - % kwargs.get('type')) + raise ValueError("Invalid parameter type: %s" % kwargs.get('type')) valid_keys = ('type', 'size', 'min', 'max', 'len_field') for k in kwargs: @@ -414,14 +397,13 @@ class BindTransmitter(Command): } # Order is important, but params dictionary is unordered - params_order = ('system_id', 'password', 'system_type', - 'interface_version', 'addr_ton', 'addr_npi', 'address_range') + params_order = ( + 'system_id', 'password', 'system_type', + 'interface_version', 'addr_ton', 'addr_npi', 'address_range', + ) def __init__(self, command, **kwargs): - """Initialize""" - - super(BindTransmitter, self).__init__(command, need_sequence=False, - **kwargs) + super(BindTransmitter, self).__init__(command, need_sequence=False, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) self.interface_version = consts.SMPP_VERSION_34 @@ -430,14 +412,12 @@ def __init__(self, command, **kwargs): class BindReceiver(BindTransmitter): """Bind as a receiver command""" def __init__(self, command, **kwargs): - """Initialize""" super(BindReceiver, self).__init__(command, **kwargs) class BindTransceiver(BindTransmitter): - """Bind as reciever and transmitter command""" + """Bind as receiver and transmitter command""" def __init__(self, command, **kwargs): - """Initialize""" super(BindTransceiver, self).__init__(command, **kwargs) @@ -452,7 +432,6 @@ class BindTransmitterResp(Command): params_order = ('system_id', 'sc_interface_version') def __init__(self, command, **kwargs): - """Initialize""" super(BindTransmitterResp, self).__init__(command, need_sequence=False, **kwargs) @@ -462,14 +441,12 @@ def __init__(self, command, **kwargs): class BindReceiverResp(BindTransmitterResp): """Response for bind as a reciever command""" def __init__(self, command, **kwargs): - """Initialize""" super(BindReceiverResp, self).__init__(command, **kwargs) class BindTransceiverResp(BindTransmitterResp): """Response for bind as a transceiver command""" def __init__(self, command, **kwargs): - """Initialize""" super(BindTransceiverResp, self).__init__(command, **kwargs) @@ -525,12 +502,13 @@ class DataSM(Command): 'alert_on_msg_delivery': Param(type=flag), 'language_indicator': Param(type=int, size=1), 'its_reply_type': Param(type=int, size=1), - 'its_session_info': Param(type=int, size=2) + 'its_session_info': Param(type=int, size=2), } - params_order = ('service_type', 'source_addr_ton', 'source_addr_npi', + params_order = ( + 'service_type', 'source_addr_ton', 'source_addr_npi', 'source_addr', 'dest_addr_ton', 'dest_addr_npi', 'destination_addr', - 'esm_class', 'registered_delivery', 'data_coding' + 'esm_class', 'registered_delivery', 'data_coding', # Optional params: 'source_port', 'source_addr_subunit', 'source_network_type', @@ -545,10 +523,10 @@ class DataSM(Command): 'user_response_code', 'display_time', 'sms_signal', 'ms_validity', 'ms_msg_wait_facilities', 'number_of_messages', 'alert_on_message_delivery', 'language_indicator', 'its_reply_type', - 'its_session_info') + 'its_session_info', + ) def __init__(self, command, **kwargs): - """Initialize""" super(DataSM, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) @@ -563,8 +541,6 @@ class DataSMResp(Command): dpf_result = None def __init__(self, command, **kwargs): - """Initialize""" - super(DataSMResp, self).__init__(command, **kwargs) @@ -574,10 +550,7 @@ class GenericNAck(Command): _defs = [] def __init__(self, command, **kwargs): - """Initialize""" - - super(GenericNAck, self).__init__(command, need_sequence=False, - **kwargs) + super(GenericNAck, self).__init__(command, need_sequence=False, **kwargs) class SubmitSM(Command): @@ -642,7 +615,7 @@ class SubmitSM(Command): # Encoding scheme of the short messaege data data_coding = None # SMPP_ENCODING_DEFAULT#ISO10646 - # Indicates the short message to send from a list of predefined + # Indicates the short message to send from a list of predefined # ('canned') short messages stored on the SMSC sm_default_msg_id = None @@ -672,8 +645,8 @@ class SubmitSM(Command): 'data_coding': Param(type=int, size=1), 'sm_default_msg_id': Param(type=int, size=1), 'sm_length': Param(type=int, size=1), - 'short_message': Param(type=ostr, max=254, - len_field='sm_length'), + 'short_message': Param(type=ostr, max=254, len_field='sm_length'), + # Optional params 'user_message_reference': Param(type=int, size=2), 'source_port': Param(type=int, size=2), @@ -704,7 +677,8 @@ class SubmitSM(Command): 'ussd_service_op': Param(type=int, size=1), } - params_order = ('service_type', 'source_addr_ton', 'source_addr_npi', + params_order = ( + 'service_type', 'source_addr_ton', 'source_addr_npi', 'source_addr', 'dest_addr_ton', 'dest_addr_npi', 'destination_addr', 'esm_class', 'protocol_id', 'priority_flag', 'schedule_delivery_time', 'validity_period', 'registered_delivery', @@ -721,10 +695,10 @@ class SubmitSM(Command): 'sms_signal', 'ms_validity', 'ms_msg_wait_facilities', 'number_of_messages', 'alert_on_message_delivery', 'language_indicator', 'its_reply_type', 'its_session_info', - 'ussd_service_op') + 'ussd_service_op', + ) def __init__(self, command, **kwargs): - """Initialize""" super(SubmitSM, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) @@ -743,15 +717,13 @@ class SubmitSMResp(Command): """Response command for submit_sm""" params = { - 'message_id': Param(type=str, max=65) + 'message_id': Param(type=str, max=65), } params_order = ('message_id',) def __init__(self, command, **kwargs): - """Initialize""" - super(SubmitSMResp, self).__init__(command, need_sequence=False, - **kwargs) + super(SubmitSMResp, self).__init__(command, need_sequence=False, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) @@ -777,8 +749,7 @@ class DeliverSM(SubmitSM): 'data_coding': Param(type=int, size=1), 'sm_default_msg_id': Param(type=int, size=1), 'sm_length': Param(type=int, size=1), - 'short_message': Param(type=ostr, max=254, - len_field='sm_length'), + 'short_message': Param(type=ostr, max=254, len_field='sm_length'), # Optional params 'user_message_reference': Param(type=int, size=2), @@ -799,9 +770,10 @@ class DeliverSM(SubmitSM): 'network_error_code': Param(type=ostr, size=3), 'message_state': Param(type=int, size=1), 'receipted_message_id': Param(type=str, max=65), - } + } - params_order = ('service_type', 'source_addr_ton', 'source_addr_npi', + params_order = ( + 'service_type', 'source_addr_ton', 'source_addr_npi', 'source_addr', 'dest_addr_ton', 'dest_addr_npi', 'destination_addr', 'esm_class', 'protocol_id', 'priority_flag', 'schedule_delivery_time', 'validity_period', 'registered_delivery', @@ -815,10 +787,10 @@ class DeliverSM(SubmitSM): 'payload_type', 'message_payload', 'callback_num', 'source_subaddress', 'dest_subaddress', 'language_indicator', 'its_session_info', - 'network_error_code', 'message_state', 'receipted_message_id') + 'network_error_code', 'message_state', 'receipted_message_id', + ) def __init__(self, command, **kwargs): - """Initialize""" super(DeliverSM, self).__init__(command, need_sequence=False, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) @@ -828,7 +800,6 @@ class DeliverSMResp(SubmitSMResp): message_id = None def __init__(self, command, **kwargs): - """Initialize""" super(DeliverSMResp, self).__init__(command, **kwargs) @@ -839,7 +810,6 @@ class Unbind(Command): params_order = () def __init__(self, command, **kwargs): - """Initialize""" super(Unbind, self).__init__(command, need_sequence=False, **kwargs) @@ -850,9 +820,7 @@ class UnbindResp(Command): params_order = () def __init__(self, command, **kwargs): - """Initialize""" - super(UnbindResp, self).__init__(command, need_sequence=False, - **kwargs) + super(UnbindResp, self).__init__(command, need_sequence=False, **kwargs) class EnquireLink(Command): @@ -861,9 +829,7 @@ class EnquireLink(Command): params_order = () def __init__(self, command, **kwargs): - """Initialize""" - super(EnquireLink, self).__init__(command, need_sequence=False, - **kwargs) + super(EnquireLink, self).__init__(command, need_sequence=False, **kwargs) class EnquireLinkResp(Command): @@ -872,14 +838,11 @@ class EnquireLinkResp(Command): params_order = () def __init__(self, command, **kwargs): - """Initialize""" - super(EnquireLinkResp, self).__init__(command, need_sequence=False, - **kwargs) + super(EnquireLinkResp, self).__init__(command, need_sequence=False, **kwargs) -class AlertNotification(Command): - """alert_notification command class - """ +class AlertNotification(Command): + """`alert_notification` command class""" # Type of Number for source address source_addr_ton = None @@ -913,14 +876,15 @@ class AlertNotification(Command): 'ms_availability_status' : Param(type=int, size=1), } - params_order = ('source_addr_ton', 'source_addr_npi', + params_order = ( + 'source_addr_ton', 'source_addr_npi', 'source_addr', 'esme_addr_ton', 'esme_addr_npi', 'esme_addr', # Optional params - 'ms_availability_status') + 'ms_availability_status', + ) def __init__(self, command, **kwargs): - """Initialize""" super(AlertNotification, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) diff --git a/smpplib/command_codes.py b/smpplib/command_codes.py index bc27582..eab6868 100644 --- a/smpplib/command_codes.py +++ b/smpplib/command_codes.py @@ -1,5 +1,6 @@ import six -from . import exceptions + +from smpplib import exceptions # # SMPP commands map (human-readable -> numeric) @@ -31,28 +32,30 @@ 'submit_multi_resp': 0x80000021, 'alert_notification': 0x00000102, 'data_sm': 0x00000103, - 'data_sm_resp': 0x80000103 + 'data_sm_resp': 0x80000103, } def get_command_name(code): - """Return command name by given code. If code is unknown, raise - UnkownCommandError exception""" + """ + Return command name by given code. + If code is unknown, raise UnknownCommandError exception. + """ for key, value in six.iteritems(commands): if value == code: return key - raise exceptions.UnknownCommandError("Unknown SMPP command code " - "'0x%x'" % code) + raise exceptions.UnknownCommandError("Unknown SMPP command code '0x%x'" % code) def get_command_code(name): - """Return command code by given command name. If name is unknown, - raise UnknownCommandError exception""" + """ + Return command code by given command name. + If name is unknown, raise UnknownCommandError exception. + """ try: return commands[name] except KeyError: - raise exceptions.UnknownCommandError("Unknown SMPP command name '%s'" - % name) + raise exceptions.UnknownCommandError("Unknown SMPP command name '%s'" % name) diff --git a/smpplib/consts.py b/smpplib/consts.py index 60dbd8c..3b73a71 100644 --- a/smpplib/consts.py +++ b/smpplib/consts.py @@ -1,8 +1,10 @@ import six -EMPTY_STRING=six.b('') -NULL_STRING=six.b('\0') +EMPTY_STRING = six.b('') +NULL_STRING = six.b('\0') + +# Message part lengths in different encodings. SEVENBIT_SIZE = 160 EIGHTBIT_SIZE = 140 UCS2_SIZE = 70 @@ -10,9 +12,8 @@ EIGHTBIT_MP_SIZE = EIGHTBIT_SIZE - 6 UCS2_MP_SIZE = UCS2_SIZE - 3 -# -# SMPP error codes: -# + +# SMPP error codes. SMPP_ESME_ROK = 0x00000000 SMPP_ESME_RINVMSGLEN = 0x00000001 SMPP_ESME_RINVCMDLEN = 0x00000002 @@ -63,9 +64,7 @@ SMPP_ESME_RUNKNOWNERR = 0x000000FF -# -# Status description strings: -# +# Status description strings. DESCRIPTIONS = { SMPP_ESME_ROK: 'No Error', SMPP_ESME_RINVMSGLEN: 'Message Length is invalid', @@ -89,8 +88,7 @@ SMPP_ESME_RINVNUMDESTS: 'Invalid number of destinations', SMPP_ESME_RINVDLNAME: 'Invalid Distribution List name', SMPP_ESME_RINVDESTFLAG: 'Invalid Destination Flag (submit_multi)', - SMPP_ESME_RINVSUBREP: 'Invalid Submit With Replace request ' - '(replace_if_present_flag set)', + SMPP_ESME_RINVSUBREP: 'Invalid Submit With Replace request (replace_if_present_flag set)', SMPP_ESME_RINVESMCLASS: 'Invalid esm_class field data', SMPP_ESME_RCNTSUBDL: 'Cannot submit to Distribution List', SMPP_ESME_RSUBMITFAIL: 'submit_sm or submit_multi failed', @@ -101,8 +99,7 @@ SMPP_ESME_RINVSYSTYP: 'Invalid system_type field', SMPP_ESME_RINVREPFLAG: 'Invalid replace_if_present flag', SMPP_ESME_RINVNUMMSGS: 'Invalid number of messages', - SMPP_ESME_RTHROTTLED: 'Throttling error (ESME has exceeded allowed ' - 'message limits)', + SMPP_ESME_RTHROTTLED: 'Throttling error (ESME has exceeded allowed message limits)', SMPP_ESME_RINVSCHED: 'Invalid Scheduled Delivery Time', SMPP_ESME_RINVEXPIRY: 'Invalid message validity period (Expiry Time)', SMPP_ESME_RINVDFTMSGID: 'Predefined Message is invalid or not found', @@ -116,18 +113,19 @@ SMPP_ESME_RMISSINGOPTPARAM: 'Expected Optional Parameter missing', SMPP_ESME_RINVOPTPARAMVAL: 'Invalid Optional Parameter Value', SMPP_ESME_RDELIVERYFAILURE: 'Delivery Failure (used data_sm_resp)', - SMPP_ESME_RUNKNOWNERR: 'Unknown Error' + SMPP_ESME_RUNKNOWNERR: 'Unknown Error', } + +# Internal client state. SMPP_CLIENT_STATE_CLOSED = 0 SMPP_CLIENT_STATE_OPEN = 1 SMPP_CLIENT_STATE_BOUND_TX = 2 SMPP_CLIENT_STATE_BOUND_RX = 3 SMPP_CLIENT_STATE_BOUND_TRX = 4 -# -# TON (Type Of Number) values -# + +# TON (Type Of Number) values. SMPP_TON_UNK = 0x00 SMPP_TON_INTL = 0x01 SMPP_TON_NATNL = 0x02 @@ -137,9 +135,7 @@ SMPP_TON_ABBREV = 0x06 -# -# NPI (Numbering Plan Indicator) values -# +# NPI (Numbering Plan Indicator) values. SMPP_NPI_UNK = 0x00 # Unknown SMPP_NPI_ISDN = 0x01 # ISDN (E163/E164) SMPP_NPI_DATA = 0x03 # Data (X.121) @@ -152,9 +148,7 @@ SMPP_NPI_WAP = 0x12 # WAP -# -# Encoding Types -# +# Encoding types. SMPP_ENCODING_DEFAULT = 0x00 # SMSC Default SMPP_ENCODING_IA5 = 0x01 # IA5 (CCITT T.50)/ASCII (ANSI X3.4) SMPP_ENCODING_BINARY = 0x02 # Octet unspecified (8-bit binary) @@ -170,9 +164,7 @@ SMPP_ENCODING_KSC5601 = 0x0E # KS C 5601 -# -# Language Types -# +# Language types. SMPP_LANG_DEFAULT = 0x00 SMPP_LANG_EN = 0x01 SMPP_LANG_FR = 0x02 @@ -180,76 +172,68 @@ SMPP_LANG_DE = 0x04 -# -# ESM class values -# +# ESM class values. SMPP_MSGMODE_DEFAULT = 0x00 # Default SMSC mode (e.g. Store and Forward) SMPP_MSGMODE_DATAGRAM = 0x01 # Datagram mode SMPP_MSGMODE_FORWARD = 0x02 # Forward (i.e. Transaction) mode -SMPP_MSGMODE_STOREFORWARD = 0x03 # Store and Forward mode (use this to - # select Store and Forward mode if Default - # mode is not Store and Forward) +SMPP_MSGMODE_STOREFORWARD = 0x03 # Explicit Store and Forward mode SMPP_MSGTYPE_DEFAULT = 0x00 # Default message type (i.e. normal message) -SMPP_MSGTYPE_DELIVERYACK = 0x08 # Message containts ESME Delivery - # Acknowledgement -SMPP_MSGTYPE_USERACK = 0x10 # Message containts ESME Manual/User - # Acknowledgement +SMPP_MSGTYPE_DELIVERYACK = 0x08 # Message containts ESME Delivery acknowledgement +SMPP_MSGTYPE_USERACK = 0x10 # Message containts ESME Manual/User acknowledgement + SMPP_GSMFEAT_NONE = 0x00 # No specific features selected SMPP_GSMFEAT_UDHI = 0x40 # UDHI Indicator (only relevant for MT msgs) SMPP_GSMFEAT_REPLYPATH = 0x80 # Set Reply Path (only relevant for GSM net) SMPP_GSMFEAT_UDHIREPLYPATH = 0xC0 # Set UDHI and Reply Path (for GSM net) -# -# SMPP Protocol ID -# + +# SMPP Protocol ID. SMPP_PID_DEFAULT = 0x00 # Default SMPP_PID_RIP = 0x41 # Replace if present on handset -# -# SMPP User Data Header Information Element Identifier -# + +# SMPP User Data Header Information Element Identifier. SMPP_UDHIEIE_CONCATENATED = 0x00 # Concatenated short message, 8-bit ref SMPP_UDHIEIE_SPECIAL = 0x01 SMPP_UDHIEIE_RESERVED = 0x02 SMPP_UDHIEIE_PORT8 = 0x04 SMPP_UDHIEIE_PORT16 = 0x04 -# -# ms_availability_status parameter from alert_notification operation -# + +# `ms_availability_status` parameter from `alert_notification` operation. SMPP_MS_AVAILABILITY_STATUS_AVAILABLE = 0x00 SMPP_MS_AVAILABILITY_STATUS_DENIED = 0x01 SMPP_MS_AVAILABILITY_STATUS_UNAVAILABLE = 0x02 -# -# registered_delivery parameter used to request an SMSC delivery receipt and/or SME originated acknowledgements -# -# -# SMSC Delivery Receipt (bits 1 and 0): -SMPP_SMSC_DELIVERY_RECEIPT_BITMASK = 0x03 -SMPP_SMSC_DELIVERY_RECEIPT_NONE = 0x00 # No SMSC Delivery Receipt requested (default) -SMPP_SMSC_DELIVERY_RECEIPT_BOTH = 0x01 # SMSC Delivery Receipt requested where final delivery outcome is delivery success or failure -SMPP_SMSC_DELIVERY_RECEIPT_FAILURE = 0x02 # SMSC Delivery Receipt requested where the final delivery outcome is delivery failure -#SME originated Acknowledgement (bits 3 and 2): -SMPP_SME_ACK_BITMASK = 0x0C # No recipient SME acknowledgment requested (default) -SMPP_SME_ACK_NONE = 0x00 # No recipient SME acknowledgment requested (default) -SMPP_SME_ACK_DELIVERY = 0x04 # SME Delivery Acknowledgement requested -SMPP_SME_ACK_MANUAL = 0x08 # SME Manual/User Acknowledgment requested -SMPP_SME_ACK_BOTH = 0x0C # Both Delivery and Manual/User Acknowledgment requested -#Intermediate Notification (bit 5): + +# `registered_delivery` parameter used to request an SMSC delivery receipt and/or SME originated acknowledgements. +# SMSC Delivery Receipt (bits 1 and 0). +SMPP_SMSC_DELIVERY_RECEIPT_NONE = 0x00 # No SMSC Delivery Receipt requested (default) +SMPP_SMSC_DELIVERY_RECEIPT_BOTH = 0x01 # SMSC Delivery Receipt requested where final delivery outcome is delivery success or failure +SMPP_SMSC_DELIVERY_RECEIPT_FAILURE = 0x02 # SMSC Delivery Receipt requested where the final delivery outcome is delivery failure +SMPP_SMSC_DELIVERY_RECEIPT_BITMASK = 0x03 # Reserved. + +# SME originated Acknowledgement (bits 3 and 2). +SMPP_SME_ACK_BITMASK = 0x0C # No recipient SME acknowledgment requested (default) +SMPP_SME_ACK_NONE = 0x00 # No recipient SME acknowledgment requested (default) +SMPP_SME_ACK_DELIVERY = 0x04 # SME Delivery Acknowledgement requested +SMPP_SME_ACK_MANUAL = 0x08 # SME Manual/User Acknowledgment requested +SMPP_SME_ACK_BOTH = 0x0C # Both Delivery and Manual/User Acknowledgment requested + +# Intermediate Notification (bit 5). SMPP_INT_NOTIFICIATION_BITMASK = 0x10 -SMPP_INT_NOTIFICIATION_NONE = 0x00 # No Intermediate notification requested (default) -SMPP_INT_NOTIFICIATION_REQUESTED = 0x10 # Intermediate notification requested +SMPP_INT_NOTIFICIATION_NONE = 0x00 # No Intermediate notification requested (default) +SMPP_INT_NOTIFICIATION_REQUESTED = 0x10 # Intermediate notification requested + -# -# SMPP protocol versions -# +# SMPP protocol versions. SMPP_VERSION_33 = 0x33 SMPP_VERSION_34 = 0x34 + COMMAND_STATES = { 'bind_transmitter': (SMPP_CLIENT_STATE_OPEN,), 'bind_transmitter_resp': (SMPP_CLIENT_STATE_OPEN,), @@ -258,60 +242,65 @@ 'bind_transceiver': (SMPP_CLIENT_STATE_OPEN,), 'bind_transceiver_resp': (SMPP_CLIENT_STATE_OPEN,), 'outbind': (SMPP_CLIENT_STATE_OPEN,), - 'unbind': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'unbind_resp': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'submit_sm': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'submit_sm_resp': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'submit_sm_multi': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'submit_sm_multi_resp': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'data_sm': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'data_sm_resp': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'deliver_sm': (SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'deliver_sm_resp': (SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'query_sm': (SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'query_sm_resp': (SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'cancel_sm': (SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'cancel_sm_resp': (SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), + 'unbind': ( + SMPP_CLIENT_STATE_BOUND_TX, + SMPP_CLIENT_STATE_BOUND_RX, + SMPP_CLIENT_STATE_BOUND_TRX, + ), + 'unbind_resp': ( + SMPP_CLIENT_STATE_BOUND_TX, + SMPP_CLIENT_STATE_BOUND_RX, + SMPP_CLIENT_STATE_BOUND_TRX, + ), + 'submit_sm': (SMPP_CLIENT_STATE_BOUND_TX, SMPP_CLIENT_STATE_BOUND_TRX), + 'submit_sm_resp': (SMPP_CLIENT_STATE_BOUND_TX, SMPP_CLIENT_STATE_BOUND_TRX), + 'submit_sm_multi': (SMPP_CLIENT_STATE_BOUND_TX, SMPP_CLIENT_STATE_BOUND_TRX), + 'submit_sm_multi_resp': (SMPP_CLIENT_STATE_BOUND_TX, SMPP_CLIENT_STATE_BOUND_TRX), + 'data_sm': ( + SMPP_CLIENT_STATE_BOUND_TX, + SMPP_CLIENT_STATE_BOUND_RX, + SMPP_CLIENT_STATE_BOUND_TRX, + ), + 'data_sm_resp': ( + SMPP_CLIENT_STATE_BOUND_TX, + SMPP_CLIENT_STATE_BOUND_RX, + SMPP_CLIENT_STATE_BOUND_TRX, + ), + 'deliver_sm': (SMPP_CLIENT_STATE_BOUND_RX, SMPP_CLIENT_STATE_BOUND_TRX), + 'deliver_sm_resp': (SMPP_CLIENT_STATE_BOUND_RX, SMPP_CLIENT_STATE_BOUND_TRX), + 'query_sm': (SMPP_CLIENT_STATE_BOUND_RX, SMPP_CLIENT_STATE_BOUND_TRX), + 'query_sm_resp': (SMPP_CLIENT_STATE_BOUND_RX, SMPP_CLIENT_STATE_BOUND_TRX), + 'cancel_sm': (SMPP_CLIENT_STATE_BOUND_RX, SMPP_CLIENT_STATE_BOUND_TRX,), + 'cancel_sm_resp': (SMPP_CLIENT_STATE_BOUND_RX, SMPP_CLIENT_STATE_BOUND_TRX,), 'replace_sm': (SMPP_CLIENT_STATE_BOUND_TX,), 'replace_sm_resp': (SMPP_CLIENT_STATE_BOUND_TX,), - 'enquire_link': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'enquire_link_resp': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'alert_notification': (SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,), - 'generic_nack': (SMPP_CLIENT_STATE_BOUND_TX, - SMPP_CLIENT_STATE_BOUND_RX, - SMPP_CLIENT_STATE_BOUND_TRX,) + 'enquire_link': ( + SMPP_CLIENT_STATE_BOUND_TX, + SMPP_CLIENT_STATE_BOUND_RX, + SMPP_CLIENT_STATE_BOUND_TRX, + ), + 'enquire_link_resp': ( + SMPP_CLIENT_STATE_BOUND_TX, + SMPP_CLIENT_STATE_BOUND_RX, + SMPP_CLIENT_STATE_BOUND_TRX, + ), + 'alert_notification': (SMPP_CLIENT_STATE_BOUND_RX, SMPP_CLIENT_STATE_BOUND_TRX), + 'generic_nack': ( + SMPP_CLIENT_STATE_BOUND_TX, + SMPP_CLIENT_STATE_BOUND_RX, + SMPP_CLIENT_STATE_BOUND_TRX, + ) } + STATE_SETTERS = { 'bind_transmitter_resp': SMPP_CLIENT_STATE_BOUND_TX, 'bind_receiver_resp': SMPP_CLIENT_STATE_BOUND_RX, 'bind_transceiver_resp': SMPP_CLIENT_STATE_BOUND_TRX, - 'unbind_resp': SMPP_CLIENT_STATE_OPEN + 'unbind_resp': SMPP_CLIENT_STATE_OPEN, } + OPTIONAL_PARAMS = { 'dest_addr_subunit': 0x0005, 'dest_network_type': 0x0006, @@ -356,5 +345,5 @@ 'ms_validity': 0x1204, 'alert_on_message_delivery': 0x130C, 'its_reply_type': 0x1380, - 'its_session_info': 0x1383 + 'its_session_info': 0x1383, } diff --git a/smpplib/gsm.py b/smpplib/gsm.py index eccb594..8a78e7b 100644 --- a/smpplib/gsm.py +++ b/smpplib/gsm.py @@ -3,8 +3,7 @@ import six -from . import consts -from . import exceptions +from smpplib import consts, exceptions def make_parts(text, encoding=consts.SMPP_ENCODING_DEFAULT): diff --git a/smpplib/pdu.py b/smpplib/pdu.py index c74324e..21712a7 100644 --- a/smpplib/pdu.py +++ b/smpplib/pdu.py @@ -24,10 +24,8 @@ import struct -from . import command_codes -from . import consts - -SMPP_ESME_ROK = 0x00000000 +from smpplib import command_codes, consts +from smpplib.consts import SMPP_ESME_ROK def extract_command(pdu): @@ -141,7 +139,6 @@ def generate(self): command_code = command_codes.get_command_code(self.command) - header = struct.pack(">LLLL", self._length, command_code, - self.status, self.sequence) + header = struct.pack(">LLLL", self._length, command_code, self.status, self.sequence) return header + body diff --git a/smpplib/smpp.py b/smpplib/smpp.py index fc1864e..4368752 100644 --- a/smpplib/smpp.py +++ b/smpplib/smpp.py @@ -22,8 +22,7 @@ """SMPP module""" -from . import pdu -from . import command +from smpplib import command, pdu def make_pdu(command_name, **kwargs): diff --git a/tests/test_gsm.py b/tests/test_gsm.py index b7c5d41..6411a20 100644 --- a/tests/test_gsm.py +++ b/tests/test_gsm.py @@ -1,11 +1,7 @@ # -*- coding: utf8 -*- -import random +import mock import pytest -try: - from unittest import mock -except ImportError: - import mock from smpplib import consts from smpplib.gsm import gsm_encode, make_parts, make_parts_encoded diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5e7bb6a --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py26, py27, py34, py35, py36, py37 + +[testenv] +commands = pytest {posargs} +deps = + pytest + mock