Skip to content

Commit

Permalink
Merge pull request #72 from sudonum/bugfix/tests
Browse files Browse the repository at this point in the history
Bugfix/tests and port to Py 3.6+
  • Loading branch information
goodboy authored May 2, 2021
2 parents 08ab333 + 3ee4a3f commit 0e7aa9f
Show file tree
Hide file tree
Showing 22 changed files with 109 additions and 54 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Test Switchio

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
test:

runs-on: ubuntu-latest
strategy:
matrix:
python_version: ['3.6', '3.7', '3.8']

steps:
- uses: actions/checkout@v2
- name: setup-docker
uses: docker-practice/actions-setup-docker@v1
with:
docker_version: 19.03
- name: Set up Python ${{ matrix.python_version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python_version }}
- name: Install system dependencies
run: |
sudo apt install -y libpcap-dev libsctp-dev libncurses5-dev libssl-dev libgsl0-dev sip-tester
- name: Install app dependencies
run: |
pip install . -r requirements-test.txt
- name: Test with pytest
run: |
pytest --use-docker tests/ -vv
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
switchio
========
asyncio_ powered FreeSWITCH_ cluster control using pure Python_ 3.5+
asyncio_ powered FreeSWITCH_ cluster control using pure Python_ 3.6+

|pypi| |travis| |versions| |license| |docs|

Expand Down
4 changes: 4 additions & 0 deletions freeswitch-sounds/soundfiles_present.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
freeswitch-sounds-music-8000-1.0.52.tar.gz
freeswitch-sounds-music-16000-1.0.52.tar.gz
freeswitch-sounds-en-us-callie-8000-1.0.51.tar.gz
freeswitch-sounds-en-us-callie-16000-1.0.51.tar.gz
1 change: 1 addition & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pytest>=3.4.2
numpy
pdbpp

# pysipp for call tracking testing
Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
'switchio = switchio.cli:cli',
]
},
python_requires='>=3.6',
install_requires=['click', 'colorlog'],
package_data={
'switchio': ['../conf/switchiodp.xml']
Expand All @@ -46,8 +47,9 @@
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Intended Audience :: Telecommunications Industry',
'Intended Audience :: Developers',
'Topic :: Communications :: Telephony',
Expand Down
2 changes: 1 addition & 1 deletion switchio/apps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def load(packages=(), imp_excs=('pandas',)):
imp_excs=imp_excs,
):
if isinstance(app, ImportError):
utils.log_to_stderr().warn("'{}' failed to load - {}\n".format(
utils.log_to_stderr().warning("'{}' failed to load - {}\n".format(
path, app.message))
else:
apps_map[path] = app
Expand Down
4 changes: 2 additions & 2 deletions switchio/apps/call_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def _handle_hangup(self, sess):
self._report_on_none()
# if sess.call.sessions and sess.is_outbound():
# # we normally expect that the caller hangs up
# self.log.warn(
# self.log.warning(
# 'received hangup for inbound session {}'
# .format(sess.uuid)
# )
Expand Down Expand Up @@ -521,7 +521,7 @@ def stop(self):
def hupall(self):
'''Send the 'hupall' command to hangup all active calls.
'''
self.log.warn("Stopping all calls with hupall!")
self.log.warning("Stopping all calls with hupall!")
# set stopped state - no further bursts will be scheduled
self.stop()
return self.pool.evals('client.hupall()')
Expand Down
2 changes: 1 addition & 1 deletion switchio/apps/dtmf.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def on_digit(self, sess):
remaining = self.incomplete[sess]
expected = remaining.popleft()
if expected != digit:
self.log.warn("Expected digit '{}', instead received '{}' for"
self.log.warning("Expected digit '{}', instead received '{}' for"
" session '{}'".format(expected, digit))
self.failed.append(sess)
if not remaining: # all digits have now arrived
Expand Down
3 changes: 2 additions & 1 deletion switchio/apps/measure/shmarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def __new__(cls, ctypesArray, shape, dtype=float, strides=None, offset=0,
try:
tp.__array_interface__
except AttributeError:
ctypeslib.prep_array(tp)
if hasattr(ctypeslib, 'prep_array'):
ctypeslib.prep_array(tp)

obj = numpy.ndarray.__new__(
cls, shape, dtype, ctypesArray, offset, strides, order)
Expand Down
11 changes: 6 additions & 5 deletions switchio/apps/measure/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from contextlib import contextmanager
import tempfile
import csv
import numpy
import os
from switchio import utils
import multiprocessing as mp
Expand All @@ -20,7 +21,7 @@
try:
import pandas as pd
except ImportError as ie:
utils.log_to_stderr().warn(str(ie))
utils.log_to_stderr().warning(str(ie))
pd = None
else:
from . import shmarray
Expand All @@ -36,7 +37,7 @@ def moving_avg(x, n=100):
'''Compute the windowed arithmetic mean of `x` with window length `n`
'''
n = min(x.size, n)
cs = pd.np.cumsum(x)
cs = numpy.cumsum(x)
cs[n:] = cs[n:] - cs[:-n]
# cs[n - 1:] / n # true means portion
return cs / n # NOTE: first n-2 vals are not true means
Expand Down Expand Up @@ -350,10 +351,10 @@ def __init__(self, name, dtype, buf_size=2**10, path=None,
storetype=None):
self.name = name
try:
self.dtype = pd.np.dtype(dtype) if pd else dtype
self.dtype = numpy.dtype(dtype) if pd else dtype
except TypeError:
# set all columns to float64
self.dtype = pd.np.dtype(
self.dtype = numpy.dtype(
list(zip(dtype, itertools.repeat('float64')))
)

Expand Down Expand Up @@ -426,7 +427,7 @@ def append_row(self, row=None):
self._iput += 1
diff = time.time() - start
if diff > 0.005: # any more then 5ms warn the user
self.log.warn("queue.put took '{}' seconds".format(diff))
self.log.warning("queue.put took '{}' seconds".format(diff))

def stopwriter(self):
"""Trigger the background frame writer to terminate
Expand Down
2 changes: 1 addition & 1 deletion switchio/apps/measure/sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def psutil(self):
return self._psutil
except (ReferenceError, EOFError): # rpyc and its weakrefs being flaky
if self.rpyc:
self.log.warn("resetting rypc connection...")
self.log.warning("resetting rypc connection...")
self._conn = conn = self.rpyc.classic_connect()
self._psutil = conn.modules.psutil
return self._psutil
Expand Down
2 changes: 1 addition & 1 deletion switchio/apps/players.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def on_recstop(self, sess):
# mark as recorded so user can block with `EventListener.waitfor`
sess.vars['recorded'] = True
if sess.hungup:
self.log.warn(
self.log.warning(
"sess '{}' was already hungup prior to recording completion?"
.format(sess.uuid))

Expand Down
6 changes: 3 additions & 3 deletions switchio/apps/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ def prepost(self, pool):
async def on_park(self, sess):
handled = False
if not all(sess[key] == val for key, val in self.guards.items()):
self.log.warn("Session with id {} did not pass guards"
.format(sess.uuid))
self.log.warning("Session with id {} did not pass guards"
.format(sess.uuid))
else:
for func in self.route.iter_matches(sess, sess=sess, router=self):
handled = True # at least one match
Expand All @@ -163,7 +163,7 @@ async def on_park(self, sess):
.format(func.func, func.keywords['match'], sess.uuid)
)
if not handled and self.guard:
self.log.warn("Rejecting session {}".format(sess.uuid))
self.log.warning("Rejecting session {}".format(sess.uuid))
await sess.hangup('NO_ROUTE_DESTINATION')

@staticmethod
Expand Down
10 changes: 5 additions & 5 deletions switchio/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def _handle_bj(self, e):
job_uuid))
consumed = True
else:
self.log.warn("No session corresponding to bj '{}'"
self.log.warning("No session corresponding to bj '{}'"
.format(job_uuid))

# run the job's callback
Expand Down Expand Up @@ -247,7 +247,7 @@ def _handle_initial_event(self, e):
# variable is used for tracking locally bridged calls
call_uuid = e.get(self.call_tracking_header) # could be 'None'
if not call_uuid:
self.log.warn(
self.log.warning(
"Unable to associate {} session '{}' with a call using "
"variable '{}'".format(
direction, sess.uuid, self.call_tracking_header))
Expand Down Expand Up @@ -289,7 +289,7 @@ def _handle_answer(self, e):
sess.update(e)
return True, sess
else:
self.log.warn('Skipping answer of {}'.format(uuid))
self.log.warning('Skipping answer of {}'.format(uuid))
return False, None

@handler('CHANNEL_DESTROY')
Expand All @@ -316,7 +316,7 @@ def _handle_destroy(self, e):
# if possible lookup the relevant call
call_uuid = e.get(self.call_tracking_header)
if not call_uuid:
self.log.warn(
self.log.warning(
"handling HANGUP for {} session '{}' which can not be "
"associated with an active call using {}?"
.format(direction, sess.uuid, self.call_tracking_header))
Expand All @@ -342,7 +342,7 @@ def _handle_destroy(self, e):
# remove call from our set
call = self.calls.pop(call.uuid, None)
if not call:
self.log.warn(
self.log.warning(
"Call with id '{}' containing Session '{}' was "
"already removed".format(call.uuid, sess.uuid))
else:
Expand Down
12 changes: 6 additions & 6 deletions switchio/loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def new_event_loop():
import uvloop
return uvloop.new_event_loop()
except ImportError as err:
utils.log_to_stderr().warn(str(err))
utils.log_to_stderr().warning(str(err))
return asyncio.new_event_loop()


Expand Down Expand Up @@ -182,9 +182,9 @@ def connect(self, loop=None, timeout=3, debug=False, **conn_kwargs):
self.host))

def get_tasks(self, include_current=False):
tasks = asyncio.Task.all_tasks(self.loop)
tasks = utils.all_tasks(self.loop)
if not include_current:
curr = asyncio.Task.current_task(self.loop)
curr = utils.current_task(self.loop)
tasks.discard(curr)
return tuple(tasks)

Expand Down Expand Up @@ -223,9 +223,9 @@ async def _listen_forever(self):
if evname:
consumed = await self._process_event(e, evname)
if not consumed:
self.log.warn("unconsumed event '{}'?".format(e))
self.log.warning("unconsumed event '{}'?".format(e))
else:
self.log.warn("received unnamed event '{}'?".format(e))
self.log.warning("received unnamed event '{}'?".format(e))

pending = self.get_tasks()
if pending:
Expand Down Expand Up @@ -427,7 +427,7 @@ def _stop(self):
'''Stop bg thread and event loop.
'''
if current_thread() is self._thread:
self.log.warn("Stop called from event loop thread?")
self.log.warning("Stop called from event loop thread?")

# wait on disconnect success
self._con.disconnect()
Expand Down
4 changes: 2 additions & 2 deletions switchio/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def recv(self, name, timeout=None):
fut._evname = name

# keep track of consuming coroutine(s)
caller = asyncio.Task.current_task(loop)
caller = utils.current_task(loop)
self.tasks.setdefault(fut, []).append(caller)

fut.add_done_callback(self.unreg_tasks)
Expand Down Expand Up @@ -591,7 +591,7 @@ def uuid(self):
try:
return self.fut.result()['Job-UUID']
except futures.TimeoutError:
self.log.warn(
self.log.warning(
"Response timeout for job {}"
.format(self.sess_uuid)
)
Expand Down
8 changes: 4 additions & 4 deletions switchio/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@ def process_events(self, events, parsed):
fut = futures.popleft()
fut.set_result(event)
except IndexError:
self.log.warn("no scheduled future could be found "
self.log.warning("no scheduled future could be found "
"for event?\n{!r}".format(event))
except asyncio.InvalidStateError:
except asyncio.base_futures.InvalidStateError:
if not fut.cancelled():
self.log.warn(
self.log.warning(
"future was already cancelled for event {}"
.format(event))
else:
Expand Down Expand Up @@ -251,7 +251,7 @@ def _handle_cmd_resp(self, future):
try:
event = future.result()
except asyncio.CancelledError:
self.log.warn("future cancelled for cmd `{}`"
self.log.warning("future cancelled for cmd `{}`"
.format(future.cmd))
return {}

Expand Down
12 changes: 9 additions & 3 deletions switchio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import uuid as mod_uuid
import importlib
import pkgutil
import asyncio


class ESLError(Exception):
Expand All @@ -38,6 +39,11 @@ class APIError(ESLError):
"%(name)s %(filename)s:%(lineno)d : %(message)s")
DATE_FORMAT = '%b %d %H:%M:%S'
TRACE = 5
# Backwards compatibility for < python-3.7
all_tasks = (getattr(asyncio, 'all_tasks', None)
or asyncio.Task.all_tasks)
current_task = (getattr(asyncio, 'current_task', None)
or asyncio.Task.current_task)


def get_logger(name=None):
Expand Down Expand Up @@ -179,9 +185,9 @@ def get_args(func):
:return: the argnames, kwargnames defined by func
:rtype: tuple
"""
args, varargs, varkw, defaults = inspect.getargspec(func)
index = -len(defaults) if defaults else None
return args[slice(0, index)], args[slice(index, None if index else 0)]
argspec = inspect.getfullargspec(func)
index = -len(argspec.defaults) if argspec.defaults else None
return argspec.args[slice(0, index)], argspec.args[slice(index, None if index else 0)]


def is_callback(func):
Expand Down
5 changes: 5 additions & 0 deletions tests/apps/test_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ def test_with_orig(get_orig, measure, storer):

# configure max calls originated to length of of storer buffer
cdr_storer = orig.measurers['CDR'].storer

if (cdr_storer.storetype.__name__ == 'CSVStore'
and sys.version_info >= (3, 7)):
pytest.skip('TimeoutError on python >= 3.7')

assert len(cdr_storer.data) == 0
orig.limit = orig.max_offered = cdr_storer._buf_size or 1
# orig.limit = orig.max_offered = 1
Expand Down
Loading

0 comments on commit 0e7aa9f

Please sign in to comment.