diff --git a/.gitignore b/.gitignore index 461a434..abea12b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ pyvenv.cfg *.egg-info man include +credentials.py # Unit test / coverage reports .coverage diff --git a/setup.py b/setup.py index 257aaf1..8d98242 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ -import os -import sys from setuptools import setup, find_packages with open("README.md", "r") as fh: @@ -35,4 +33,4 @@ setup( "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", ], -) \ No newline at end of file +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 0000000..fdb696a --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,80 @@ +from __future__ import absolute_import +from __future__ import print_function +from unittest.mock import patch +from voipms.api import Client +import credentials + +try: + from unittest.mock import MagicMock +except: + from mock import MagicMock + + +def test_instantiation(): + """Test instantiation of NOAA class. + """ + username = credentials.username + password = credentials.password + + client = Client(username, password) + return client + +@patch('voipms.api.general.balance') +def test_make_get_request(mock_requests): + mock_response_obj = MagicMock() + mock_response_obj.json = {"test":"test"} + mock_response_obj.status_code = 200 + mock_requests.get.return_value = mock_response_obj + + username = credentials.username + password = credentials.password + + client = Client(username, password) + + res = client.registration_status.fetch() + assert res == {"test": "test"} + +# @patch('noaa_sdk.util.requests') +# def test_make_get_request_failed(mock_requests): +# mock_response_obj = MagicMock() +# mock_response_obj.text = 'mock text' +# mock_response_obj.status_code = 500 +# mock_response_obj.json = lambda: {"test":"test"} +# mock_requests.get.return_value = mock_response_obj + +# with pytest.raises(Exception) as err: +# n = noaa.NOAA(user_agent='test_agent') +# n.make_get_request('http://test') +# assert err == 'Error: end_point is None.' + + +# @patch('noaa_sdk.noaa.NOAA.make_get_request') +# def test_points(mock_make_get_request): +# mock_make_get_request.return_value = None +# n = noaa.NOAA(user_agent='test_agent') +# n.points('23.44,34.55') +# mock_make_get_request.assert_called_with( +# '/points/23.44,34.55', end_point=n.DEFAULT_END_POINT) + + +# @patch('noaa_sdk.noaa.NOAA.make_get_request') +# def test_points_with_stations(mock_make_get_request): +# mock_make_get_request.return_value = None +# n = noaa.NOAA(user_agent='test_agent') +# n.points('23.44,34.55', stations=True) +# mock_make_get_request.assert_called_with( +# '/points/23.44,34.55/stations', end_point=n.DEFAULT_END_POINT) + + +# @patch('noaa_sdk.noaa.NOAA.make_get_request') +# def test_points_forecast(mock_make_get_request): +# mock_make_get_request.return_value = { +# 'properties': { +# 'forecast': 'forecast_uri', +# 'forecastHourly': 'forecast_hourly_uri' +# } +# } +# n = noaa.NOAA(user_agent='test_agent') +# n.points_forecast(23.44, 34.55, hourly=False) +# mock_make_get_request.assert_any_call( +# uri='forecast_uri', end_point=n.DEFAULT_END_POINT) \ No newline at end of file diff --git a/voipms/api/__init__.py b/voipms/api/__init__.py index 30fa38d..62e7af8 100644 --- a/voipms/api/__init__.py +++ b/voipms/api/__init__.py @@ -5,19 +5,18 @@ import requests from voipms.base.exceptions import VoipException + class Client(object): def __init__(self, username=None, password=None): - self.username = username or os.environ.get('VOIPMS_ACCOUNT_USER') self.password = password or os.environ.get('VOIPMS_API_TOKEN') self.api_base = "https://voip.ms/api/v1/rest.php" if not self.username or not self.password: raise VoipException("Credentials are required to create a Client") - + self.auth = (self.username, self.password) - # Domains self._accounts = None self._call_detail_records = None self._dids = None @@ -26,7 +25,7 @@ class Client(object): def request(self, method, auth=None, params={}): auth = auth or self.auth - + params["api_username"] = auth[0] params["api_password"] = auth[1] params["method"] = method @@ -34,7 +33,7 @@ class Client(object): response = requests.get(self.api_base, params=params) data = json.loads(response.text) - + if data['status'] and data['status'] != 'success': err_code = data['status'] raise VoipException(err_code) @@ -79,7 +78,7 @@ class Client(object): @property def balance(self): return self.general.balance - + @property def ip(self): return self.general.ip @@ -130,4 +129,4 @@ class Client(object): @property def messages(self): - return self.voicemail.messages \ No newline at end of file + return self.voicemail.messages diff --git a/voipms/api/accounts/__init__.py b/voipms/api/accounts/__init__.py index 96e8103..b33448e 100644 --- a/voipms/api/accounts/__init__.py +++ b/voipms/api/accounts/__init__.py @@ -1,6 +1,7 @@ from voipms.api.accounts.subaccount import Subaccount from voipms.api.accounts.registration_status import RegistrationStatus + class Accounts(): def __init__(self, base): self._subaccount = None @@ -18,4 +19,4 @@ class Accounts(): def registration_status(self): if self._registration_status is None: self._registration_status = RegistrationStatus(self.base) - return self._registration_status \ No newline at end of file + return self._registration_status diff --git a/voipms/api/accounts/registration_status.py b/voipms/api/accounts/registration_status.py index 6f7466d..a69daa5 100644 --- a/voipms/api/accounts/registration_status.py +++ b/voipms/api/accounts/registration_status.py @@ -2,6 +2,6 @@ class RegistrationStatus(): def __init__(self, base): self.method = "getRegistrationStatus" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/accounts/subaccount.py b/voipms/api/accounts/subaccount.py index 7fe45c5..a5d1324 100644 --- a/voipms/api/accounts/subaccount.py +++ b/voipms/api/accounts/subaccount.py @@ -2,19 +2,19 @@ class Subaccount(): def __init__(self, base): self.method = "" self.base = base - + def create(self, params={}): self.method = "createSubAccount" return self.base.request(self.method, params=params) - + def delete(self, params={}): self.method = "delSubAccount" return self.base.request(self.method, params=params) - + def fetch(self, params={}): self.method = "getSubAccounts" return self.base.request(self.method, params=params) def set(self, params={}): self.method = "setSubAccount" - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/call_detail_records/__init__.py b/voipms/api/call_detail_records/__init__.py index 48a7da6..330b50e 100644 --- a/voipms/api/call_detail_records/__init__.py +++ b/voipms/api/call_detail_records/__init__.py @@ -3,9 +3,9 @@ from voipms.api.call_detail_records.records import Records from voipms.api.call_detail_records.rates import Rates from voipms.api.call_detail_records.termination_rates import TerminationRates + class CallDetailRecords(): def __init__(self, base): - self.base = base self._billing = None self._records = None @@ -34,4 +34,4 @@ class CallDetailRecords(): def termination_rates(self): if self._termination_rates is None: self._termination_rates = TerminationRates(self.base) - return self._termination_rates \ No newline at end of file + return self._termination_rates diff --git a/voipms/api/call_detail_records/billing.py b/voipms/api/call_detail_records/billing.py index 3b86e41..2745272 100644 --- a/voipms/api/call_detail_records/billing.py +++ b/voipms/api/call_detail_records/billing.py @@ -2,6 +2,6 @@ class Billing(): def __init__(self, base): self.method = "getCallBilling" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/call_detail_records/rates.py b/voipms/api/call_detail_records/rates.py index cf778bb..199437e 100644 --- a/voipms/api/call_detail_records/rates.py +++ b/voipms/api/call_detail_records/rates.py @@ -2,6 +2,6 @@ class Rates(): def __init__(self, base): self.method = "getRates" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/call_detail_records/records.py b/voipms/api/call_detail_records/records.py index b221340..e0d78ce 100644 --- a/voipms/api/call_detail_records/records.py +++ b/voipms/api/call_detail_records/records.py @@ -2,6 +2,6 @@ class Records(): def __init__(self, base): self.method = "getCDR" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/call_detail_records/termination_rates.py b/voipms/api/call_detail_records/termination_rates.py index 377ac86..9fa7fb8 100644 --- a/voipms/api/call_detail_records/termination_rates.py +++ b/voipms/api/call_detail_records/termination_rates.py @@ -2,6 +2,6 @@ class TerminationRates(): def __init__(self, base): self.method = "getTerminationRates" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/dids/__init__.py b/voipms/api/dids/__init__.py index 49f57bd..0edd28a 100644 --- a/voipms/api/dids/__init__.py +++ b/voipms/api/dids/__init__.py @@ -1,6 +1,7 @@ from voipms.api.dids.search import Search from voipms.api.dids.sms import SMS + class DIDs(): def __init__(self, base): self._search = None @@ -30,4 +31,4 @@ class DIDs(): def fetch(self, params={}): self.method = "getDIDsInfo" - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/dids/search.py b/voipms/api/dids/search.py index f1a2e6b..e1a7aec 100644 --- a/voipms/api/dids/search.py +++ b/voipms/api/dids/search.py @@ -2,11 +2,11 @@ class Search(): def __init__(self, base): self.method = "" self.base = base - + def canada(self, params={}): self.method = "searchDIDsCAN" return self.base.request(self.method, params=params) def usa(self, params={}): self.method = "searchDIDsUSA" - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/dids/sms.py b/voipms/api/dids/sms.py index 8d2c2b8..e97458e 100644 --- a/voipms/api/dids/sms.py +++ b/voipms/api/dids/sms.py @@ -2,7 +2,7 @@ class SMS(): def __init__(self, base): self.method = "" self.base = base - + def fetch(self, params={}): self.method = "getSMS" return self.base.request(self.method, params=params) @@ -13,4 +13,4 @@ class SMS(): def set(self, params={}): self.method = "setSMS" - return self.base.request(self.method, params={}) \ No newline at end of file + return self.base.request(self.method, params={}) diff --git a/voipms/api/general/__init__.py b/voipms/api/general/__init__.py index ddd3b34..c29e51d 100644 --- a/voipms/api/general/__init__.py +++ b/voipms/api/general/__init__.py @@ -4,6 +4,7 @@ from voipms.api.general.transaction_history import TransactionHistory from voipms.api.general.countries import Countries from voipms.api.general.languages import Languages + class General(): def __init__(self, base): self._balance = None @@ -42,4 +43,4 @@ class General(): def languages(self): if self._languages is None: self._languages = Languages(self.base) - return self._languages \ No newline at end of file + return self._languages diff --git a/voipms/api/general/balance.py b/voipms/api/general/balance.py index e6f5f35..fa088f1 100644 --- a/voipms/api/general/balance.py +++ b/voipms/api/general/balance.py @@ -2,6 +2,6 @@ class Balance(): def __init__(self, base): self.method = "getBalance" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/general/countries.py b/voipms/api/general/countries.py index e1eb06e..8f79212 100644 --- a/voipms/api/general/countries.py +++ b/voipms/api/general/countries.py @@ -2,6 +2,6 @@ class Countries(): def __init__(self, base): self.method = "getCountries" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/general/ip.py b/voipms/api/general/ip.py index 28b02fe..fcdf5a0 100644 --- a/voipms/api/general/ip.py +++ b/voipms/api/general/ip.py @@ -2,6 +2,6 @@ class IP(): def __init__(self, base): self.method = "getIP" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/general/languages.py b/voipms/api/general/languages.py index 6040cf9..932fff3 100644 --- a/voipms/api/general/languages.py +++ b/voipms/api/general/languages.py @@ -2,6 +2,6 @@ class Languages(): def __init__(self, base): self.method = "getLanguages" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/general/transaction_history.py b/voipms/api/general/transaction_history.py index 05f43f7..02abd03 100644 --- a/voipms/api/general/transaction_history.py +++ b/voipms/api/general/transaction_history.py @@ -2,6 +2,6 @@ class TransactionHistory(): def __init__(self, base): self.method = "getTransactionHistory" self.base = base - + def fetch(self, params={}): - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/voicemail/__init__.py b/voipms/api/voicemail/__init__.py index ff06e20..b2cf3f6 100644 --- a/voipms/api/voicemail/__init__.py +++ b/voipms/api/voicemail/__init__.py @@ -1,5 +1,6 @@ from voipms.api.voicemail.messages import Messages + class Voicemail(): def __init__(self, base): self._messages = None @@ -25,4 +26,4 @@ class Voicemail(): def set(self, params={}): self.method = "setVoicemail" - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/api/voicemail/messages.py b/voipms/api/voicemail/messages.py index 7803ad5..58891fb 100644 --- a/voipms/api/voicemail/messages.py +++ b/voipms/api/voicemail/messages.py @@ -2,11 +2,11 @@ class Messages(): def __init__(self, base): self.method = "" self.base = base - + def fetch(self, params={}): self.method = "getVoicemailMessages" return self.base.request(self.method, params=params) def delete(self, params={}): self.method = "delMessages" - return self.base.request(self.method, params=params) \ No newline at end of file + return self.base.request(self.method, params=params) diff --git a/voipms/base/exceptions.py b/voipms/base/exceptions.py index a8b08ef..d3a7224 100644 --- a/voipms/base/exceptions.py +++ b/voipms/base/exceptions.py @@ -1,30 +1,33 @@ from __future__ import absolute_import, division, print_function -import sys class VoipException(Exception): def __init__(self, err_code=""): self.err_code = err_code + def __str__(self): err_code_map = { 'account_with_dids': 'The Account has DIDs assigned to it.', 'api_not_enabled': 'API has not been enabled or has been disabled', 'api_limit_exceeded': 'API requests limit per minute has been reached', - 'cancel_failed': "The cancellation wasn't completed.", 'can_have_only_one_profile_without_pin': 'The conference can just have one profile member without pin', + 'cancel_failed': "The cancellation wasn't completed.", + 'can_have_only_one_profile_without_pin': 'The conference can just have one profile member without pin', 'conference_member_relation_not_found': 'There is no relation between the profile member and the conference.', 'did_in_use': 'DID Number is already in use', 'duplicated_pin': 'The given pin has been duplicated', 'error_deleting_msg': 'Error when deleting message', 'error_moving_msg': 'Error when move the voicemail message to folder', - 'existing_did': "You can't set a callback to an existing VoIP.ms DID number", 'exceeds_file_size': 'The file exceeds the limite size allowed.', + 'existing_did': "You can't set a callback to an existing VoIP.ms DID number", + 'exceeds_file_size': 'The file exceeds the limite size allowed.', 'forwards_exceeded': 'Your account is limited to 4 forward entries', 'invalid_account': 'This is not a valid account', 'invalid_address': 'Address is missing or the format is invalid.', 'invalid_admin': 'This is not a valid admin', 'invalid_agent_ring_timeout': 'This is not a valid Agent ring time out value', 'invalid_allowedcodecs': 'One of the codecs provided is invalidFormat and Values: ulaw;g729;gsm;all', - 'invalid_attachid': "The given ID is invalid or doesn't exist.", 'invalid_announce_join_leave': 'This is not a valid "Announce join leave"', + 'invalid_attachid': "The given ID is invalid or doesn't exist.", + 'invalid_announce_join_leave': 'This is not a valid "Announce join leave"', 'invalid_announce_only_user': 'This is not a valid "Announce only user"', 'invalid_announce_position_frequency': 'This is not a valid Announce position frequency', 'invalid_announce_round_seconds': 'This is not a valid "Announce round seconds"', @@ -54,7 +57,8 @@ class VoipException(Exception): 'invalid_conference': 'This is not a valid Conference ID', 'invalid_countryid': 'This is not a valid Country ID', 'invalid_city': 'City is missing or the format is invalid.', - 'invalid_country': 'Country is missing or the format is invalid, must be in format ISO 3166-1 alpha-2, example: US, CA, etc. (You can use the values returned by the method getCountries)', + 'invalid_country': ('Country is missing or the format is invalid, must be in format ISO 3166-1 alpha-2, ' + 'example: US, CA, etc. (You can use the values returned by the method getCountries)'), 'invalid_credentials': 'Username or Password is incorrect', 'invalid_date': 'This is not a valid dateFormat is: yyyy-mm-dd', 'invalid_datetime': 'This is not a valid datetimeFormat is: yyyy-mm-dd hh:mm:ss', @@ -105,7 +109,9 @@ class VoipException(Exception): 'invalid_ip_iax2': 'Do not provide an IP address for IAX2', 'invalid_ivr': 'This is not a valid IVR', 'invalid_jitter_buffer': 'This is not a valid "jitter buffer" value', - 'invalid_join_empty_type': "This is not a valid 'JoinWhenEmpty' Type for a Queue", 'invalid_join_announcement': "This is not a valid 'Join Announcement' Type for a Queue", 'invalid_language': 'This is not a valid LanguageShould be: es/en/fr', + 'invalid_join_empty_type': "This is not a valid 'JoinWhenEmpty' Type for a Queue", + 'invalid_join_announcement': "This is not a valid 'Join Announcement' Type for a Queue", + 'invalid_language': 'This is not a valid LanguageShould be: es/en/fr', 'invalid_lastname': 'Lastname is missing or the format is invalid.', 'invalid_listened': 'This is not a valid Listened value', 'invalid_location': 'This is not a valid Location', @@ -228,7 +234,7 @@ class VoipException(Exception): 'invalid_urgent': 'This is not valid urgent value', 'invalid_zip': 'Zip Code is missing or the format is invalid.', 'ip_not_enabled': 'This IP is not enabled for API use', - 'limit_reached': 'You have reached the maximum number of messages allowed per day.- SMS limit using the API.- Fax limit applies using any method.', + 'limit_reached': 'You have reached the maximum number of messages allowed per day.', 'max_phonebook': 'Your account is limited to 8 SIP, IAX or SIP URI members', 'member_already_included': 'The member has been included already', 'members_exceeded': 'You have reached the maximum allowed entries for the Phonebook', @@ -284,13 +290,16 @@ class VoipException(Exception): 'missing_folder': 'folder was not provided', 'missing_forwarding': 'Forwarding was not provided', 'missing_id': 'ID was not provided', - 'missing_if_announce_position_enabled_report_estimated_hold_time': "'If announce position enabled report estimated hold time' type was not provided", 'missing_internationalroute': 'International Route was not provided', + 'missing_if_announce_position_enabled_report_estimated_hold_time': "'If announce position enabled report estimated hold time' type was not provided", + 'missing_internationalroute': 'International Route was not provided', 'missing_ip': 'You need to provide an IP if you select IP Authentication Method', 'missing_ip_h323': 'You must enter an IP Address for H.323', 'missing_ivr': 'IVR was not provided', - 'missing_join_when_empty': "'JoinWhenEmpty' type was not provided", 'missing_language': 'Language was not provided', + 'missing_join_when_empty': "'JoinWhenEmpty' type was not provided", + 'missing_language': 'Language was not provided', 'missing_lastname': 'Lastname was not provided', - 'missing_leave_when_empty': "'LeaveWhenEmpty' type was not provided", 'missing_listened': 'Listened code was not provided', + 'missing_leave_when_empty': "'LeaveWhenEmpty' type was not provided", + 'missing_listened': 'Listened code was not provided', 'missing_location': 'Location was not provided', 'missing_lockinternational': 'Lock International was not provided', 'missing_mailbox': 'Mailbox was not provided', @@ -320,7 +329,9 @@ class VoipException(Exception): 'missing_query': 'Query was not provided', 'missing_recording': 'Recording was not provided', 'missing_report_hold_time_agent': 'Report hold time agent was not provided', - 'missing_resellerclient': "Provide a Reseller Client or don't provide a Reseller Package", 'missing_resellerpackage': "Provide a Reseller Package or don't provide a Reseller Client", 'missing_response_timeout': 'ResponseTimeOut was not provided', + 'missing_resellerclient': "Provide a Reseller Client or don't provide a Reseller Package", + 'missing_resellerpackage': "Provide a Reseller Package or don't provide a Reseller Client", + 'missing_response_timeout': 'ResponseTimeOut was not provided', 'missing_ringgroup': 'Ring group was not provided', 'missing_ring_inuse': 'Ring in use was not provided', 'missing_ring_strategy': 'Ring strategy was not provided', @@ -358,7 +369,7 @@ class VoipException(Exception): 'no_base64file': 'File not encoded in base64', 'no_callback': 'There are not Callbacks', 'no_callhunting': 'There are no Call Huntings', - 'no_callstatus': 'No Call Status was provided.One of the following parameters needs to be set to "1": answered, noanswer, busy, failed', + 'no_callstatus': 'No Call Status was provided. One of the following parameters needs to be set to "1": answered, noanswer, busy, failed', 'no_cdr': 'There are no CDR entries for the filter', 'no_change_billingtype': 'Imposible change DID billing plan', 'no_client': 'There are no Clients', @@ -383,7 +394,8 @@ class VoipException(Exception): 'no_sms': 'There are no SMS messages', 'no_timecondition': 'There are no Time Conditions', 'note_toolong': 'The note exceeds character size limit', - 'order_failed': "The order wasn't completed.", 'provider_outofservice': 'One of our providers is out of service', + 'order_failed': "The order wasn't completed.", + 'provider_outofservice': 'One of our providers is out of service', 'recording_in_use_did': 'You have a DID using this Recording', 'recording_in_use_queue': 'You have a Calling Queue using this Recording', 'recording_in_use_ivr': 'You have an IVR using this Recording', @@ -392,7 +404,9 @@ class VoipException(Exception): 'repeated_ip': 'You already have a Subaccount using this IP and Protocol', 'reserved_ip': 'This is a reserved IP used by VoIP.ms or other Companies', 'same_did_billingtype': 'The Billing Type provided and DID billing type are the same', - 'sipuri_in_phonebook': "This SIPURI can't be deleted, it is mapped in the phonebook", 'sent_fail': "The Fax Message it wasn't send.", 'sms_toolong': 'The SMS message exceeds 160 characters', + 'sipuri_in_phonebook': "This SIPURI can't be deleted, it is mapped in the phonebook", + 'sent_fail': "The Fax Message it wasn't send.", + 'sms_toolong': 'The SMS message exceeds 160 characters', 'sms_failed': 'The SMS message was not sent', 'tls_error': 'Theres was a TLS error, please try later.', 'Unable_to_purchase': 'Unable to purchase DIDs', @@ -411,8 +425,7 @@ class VoipException(Exception): err_desc = err_code_map[self.err_code] or self.err_code return "API Call failed as: {}".format(err_desc) - class VoipRestException(VoipException): def __str__(self): - return self.err_code or "" \ No newline at end of file + return self.err_code or "" diff --git a/voipms/base/values.py b/voipms/base/values.py index b98d915..da23c2d 100644 --- a/voipms/base/values.py +++ b/voipms/base/values.py @@ -8,4 +8,4 @@ def of(d): :param dict d: A dict to strip. :return dict: A dict with unset values removed. """ - return {k: v for k, v in d.items() if v != unset} \ No newline at end of file + return {k: v for k, v in d.items() if v != unset}