diff --git a/pysipp/__init__.py b/pysipp/__init__.py index d88bdfa..5476559 100644 --- a/pysipp/__init__.py +++ b/pysipp/__init__.py @@ -15,9 +15,9 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Authors : Tyler Goodlet -''' +""" pysipp - a python wrapper for launching SIPp -''' +""" import sys from os.path import dirname from . import plugin, netplug, agent @@ -25,14 +25,13 @@ from .agent import client, server -__package__ = 'pysipp' -__author__ = 'Tyler Goodlet (tgoodlet@gmail.com)' +__package__ = "pysipp" +__author__ = "Tyler Goodlet (tgoodlet@gmail.com)" -__all__ = ['walk', 'client', 'server', 'plugin'] +__all__ = ["walk", "client", "server", "plugin"] -def walk(rootpath, delay_conf_scen=False, autolocalsocks=True, - **scenkwargs): +def walk(rootpath, delay_conf_scen=False, autolocalsocks=True, **scenkwargs): """SIPp scenario generator. Build and return scenario objects for each scenario directory. @@ -48,23 +47,21 @@ def walk(rootpath, delay_conf_scen=False, autolocalsocks=True, assert dirname(confpy.__file__) == path # predicate hook based filtering - res = hooks.pysipp_load_scendir( - path=path, xmls=xmls, confpy=confpy) + res = hooks.pysipp_load_scendir(path=path, xmls=xmls, confpy=confpy) if res and not all(res): continue agents = [] for xml in xmls: - if 'uac' in xml.lower(): + if "uac" in xml.lower(): ua = agent.client(scen_file=xml) agents.append(ua) - elif 'uas' in xml.lower(): + elif "uas" in xml.lower(): ua = agent.server(scen_file=xml) agents.insert(0, ua) # servers are always launched first else: raise ValueError( - "xml script must contain one of 'uac' or 'uas':\n{}" - .format(xml) + "xml script must contain one of 'uac' or 'uas':\n{}".format(xml) ) if delay_conf_scen: @@ -81,17 +78,13 @@ def walk(rootpath, delay_conf_scen=False, autolocalsocks=True, yield path, scen -def scenario(dirpath=None, proxyaddr=None, autolocalsocks=True, - **scenkwargs): +def scenario(dirpath=None, proxyaddr=None, autolocalsocks=True, **scenkwargs): """Return a single Scenario loaded from `dirpath` if provided else the basic default call flow. """ if dirpath: # deliver single scenario from dir - path, scen = next( - walk(dirpath, autolocalsocks=autolocalsocks, - **scenkwargs) - ) + path, scen = next(walk(dirpath, autolocalsocks=autolocalsocks, **scenkwargs)) else: with plugin.register([netplug] if autolocalsocks else []): # deliver the default scenario bound to loopback sockets @@ -100,13 +93,13 @@ def scenario(dirpath=None, proxyaddr=None, autolocalsocks=True, # same as above scen = plugin.mng.hook.pysipp_conf_scen_protocol( - agents=[uas, uac], confpy=None, + agents=[uas, uac], + confpy=None, scenkwargs=scenkwargs, ) if proxyaddr is not None: - assert isinstance(proxyaddr, tuple), ( - 'proxyaddr must be a (addr, port) tuple') + assert isinstance(proxyaddr, tuple), "proxyaddr must be a (addr, port) tuple" scen.clientdefaults.proxyaddr = proxyaddr return scen @@ -123,8 +116,7 @@ def pysipp_load_scendir(path, xmls, confpy): @plugin.hookimpl def pysipp_conf_scen_protocol(agents, confpy, scenkwargs): - """Perform default configuration rule set - """ + """Perform default configuration rule set""" # more sanity if confpy: ua = agents[0] @@ -138,13 +130,19 @@ def pysipp_conf_scen_protocol(agents, confpy, scenkwargs): scen = agent.Scenario(agents, confpy=confpy) # order the agents for launch - agents = list(hooks.pysipp_order_agents( - agents=scen.agents, clients=scen.clients, - servers=scen.servers)) or agents + agents = ( + list( + hooks.pysipp_order_agents( + agents=scen.agents, clients=scen.clients, servers=scen.servers + ) + ) + or agents + ) # create scenario wrapper scen = hooks.pysipp_new_scen( - agents=agents, confpy=confpy, scenkwargs=scenkwargs) + agents=agents, confpy=confpy, scenkwargs=scenkwargs + ) # configure scenario hooks.pysipp_conf_scen(agents=scen.agents, scen=scen) @@ -158,10 +156,8 @@ def pysipp_conf_scen_protocol(agents, confpy, scenkwargs): @plugin.hookimpl def pysipp_order_agents(agents, clients, servers): - """Lexicographically sort agents by name and always start servers first - """ - return (agents[name] for name in - sorted(servers) + sorted(clients)) + """Lexicographically sort agents by name and always start servers first""" + return (agents[name] for name in sorted(servers) + sorted(clients)) @plugin.hookimpl @@ -171,14 +167,13 @@ def pysipp_new_scen(agents, confpy, scenkwargs): @plugin.hookimpl(trylast=True) def pysipp_conf_scen(agents, scen): - """Default validation logic and routing with media - """ + """Default validation logic and routing with media""" if scen.servers: # point all clients to send requests to 'primary' server agent # if they aren't already - servers_addr = scen.serverdefaults.get('srcaddr', ('127.0.0.1', 5060)) + servers_addr = scen.serverdefaults.get("srcaddr", ("127.0.0.1", 5060)) uas = scen.prepare_agent(list(scen.servers.values())[0]) - scen.clientdefaults.setdefault('destaddr', uas.srcaddr or servers_addr) + scen.clientdefaults.setdefault("destaddr", uas.srcaddr or servers_addr) elif not scen.clientdefaults.proxyaddr: # no servers in scenario so point proxy addr to remote socket addr diff --git a/pysipp/agent.py b/pysipp/agent.py index de13a76..84a9f60 100644 --- a/pysipp/agent.py +++ b/pysipp/agent.py @@ -1,6 +1,6 @@ -''' +""" Wrappers for user agents which apply sensible cmdline arg defaults -''' +""" from os import path import re import itertools @@ -16,10 +16,11 @@ log = utils.get_logger() -SocketAddr = namedtuple('SocketAddr', 'ip port') +SocketAddr = namedtuple("SocketAddr", "ip port") DEFAULT_RUNNER_TIMEOUT = 180 + def tuple_property(attrs): def getter(self): tup = tuple(getattr(self, attr) for attr in attrs) @@ -36,7 +37,7 @@ def setter(self, pair): for attr, val in zip(attrs, pair): setattr(self, attr, val) - doc = "{} parameters composed as a tuple".format(', '.join(attrs)) + doc = "{} parameters composed as a tuple".format(", ".join(attrs)) return property(getter, setter, doc=doc) @@ -46,10 +47,11 @@ class UserAgent(command.SippCmd): higher level attributes for assigning input arguments similar to configuration options for a SIP UA. """ + # we skip `error` since we can get it from stderr - _log_types = 'screen log'.split() - _debug_log_types = 'calldebug message'.split() - _to_console = 'screen' + _log_types = "screen log".split() + _debug_log_types = "calldebug message".split() + _to_console = "screen" @property def name(self): @@ -58,98 +60,91 @@ def name(self): """ return self.scen_name or path2namext(self.scen_file) or str(None) - srcaddr = tuple_property(('local_host', 'local_port')) - destaddr = tuple_property(('remote_host', 'remote_port')) - mediaaddr = tuple_property(('media_addr', 'media_port')) - proxyaddr = tuple_property(('proxy_host', 'proxy_port')) - ipcaddr = tuple_property(('ipc_host', 'ipc_port')) - call_load = tuple_property(('rate', 'limit', 'call_count')) - + srcaddr = tuple_property(("local_host", "local_port")) + destaddr = tuple_property(("remote_host", "remote_port")) + mediaaddr = tuple_property(("media_addr", "media_port")) + proxyaddr = tuple_property(("proxy_host", "proxy_port")) + ipcaddr = tuple_property(("ipc_host", "ipc_port")) + call_load = tuple_property(("rate", "limit", "call_count")) def __call__(self, *args, **kwargs): return self.run(*args, **kwargs) - def run( - self, - timeout=180, - **kwargs - ): + def run(self, timeout=180, **kwargs): # create and configure a temp scenario scen = plugin.mng.hook.pysipp_conf_scen_protocol( - agents=[self], confpy=None, scenkwargs={}, + agents=[self], + confpy=None, + scenkwargs={}, ) return scen.run(timeout=timeout, **kwargs) def is_client(self): - return 'uac' in self.name.lower() + return "uac" in self.name.lower() def is_server(self): - return 'uas' in self.name.lower() + return "uas" in self.name.lower() - def iter_logfile_items( - self, types_attr='_log_types', enable_screen_file=True - ): + def iter_logfile_items(self, types_attr="_log_types", enable_screen_file=True): for name in getattr(self, types_attr): - if name != 'screen' or enable_screen_file: - attr_name = name + '_file' + if name != "screen" or enable_screen_file: + attr_name = name + "_file" yield attr_name, getattr(self, attr_name) def iter_toconsole_items(self): - yield 'screen_file', self.screen_file + yield "screen_file", self.screen_file @property def cmd(self): - """Rendered SIPp command string - """ + """Rendered SIPp command string""" return self.render() @property def logdir(self): - return getattr(self, '_logdir', None) + return getattr(self, "_logdir", None) @logdir.setter def logdir(self, dirpath): - assert path.isdir(dirpath), '{} is an invalid path'.format(dirpath) + assert path.isdir(dirpath), "{} is an invalid path".format(dirpath) self._logdir = dirpath @property - def plays_media(self, patt='play_pcap_audio'): - """Bool determining whether script plays media - """ + def plays_media(self, patt="play_pcap_audio"): + """Bool determining whether script plays media""" # TODO: should be able to parse using -sd if not self.scen_file: return False - with open(self.scen_file, 'r') as sf: + with open(self.scen_file, "r") as sf: return bool(re.search(patt, sf.read())) def enable_tracing(self): - """Enable trace flags for this command - """ + """Enable trace flags for this command""" for name in self._log_types: - attr_name = 'trace_' + name + attr_name = "trace_" + name setattr(self, attr_name, True) - def enable_logging( - self, logdir=None, debug=False, enable_screen_file=True - ): + def enable_logging(self, logdir=None, debug=False, enable_screen_file=True): """Enable agent logging by appending appropriately named log file arguments to the underlying command. """ - logattrs = self.iter_logfile_items( - enable_screen_file=enable_screen_file) + logattrs = self.iter_logfile_items(enable_screen_file=enable_screen_file) if debug: logattrs = itertools.chain( logattrs, - self.iter_logfile_items('_debug_log_types'), + self.iter_logfile_items("_debug_log_types"), ) # prefix all log file paths for name, attr in logattrs: setattr( - self, name, attr or path.join( + self, + name, + attr + or path.join( logdir or self.logdir or tempfile.gettempdir(), - "{}_{}".format(self.name, name)) + "{}_{}".format(self.name, name), + ), ) self.enable_tracing() @@ -167,11 +162,11 @@ def ua(logdir=None, **kwargs): Returns a command string instance with sensible default arguments. """ defaults = { - 'bin_path': spawn.find_executable('sipp'), + "bin_path": spawn.find_executable("sipp"), } # drop any built-in scen if a script file is provided - if 'scen_file' in kwargs: - kwargs.pop('scen_name', None) + if "scen_file" in kwargs: + kwargs.pop("scen_name", None) # override with user settings defaults.update(kwargs) @@ -189,12 +184,10 @@ def server(**kwargs): (i.e. recieves a SIP message as it's first action) """ defaults = { - 'scen_name': 'uas', + "scen_name": "uas", } - if 'dstaddr' in kwargs: - raise ValueError( - "User agent server does not accept a destination address" - ) + if "dstaddr" in kwargs: + raise ValueError("User agent server does not accept a destination address") # override with user settings defaults.update(kwargs) return ua(**defaults) @@ -205,7 +198,7 @@ def client(**kwargs): (i.e. sends a SIP message as it's first action) """ defaults = { - 'scen_name': 'uac', + "scen_name": "uac", } # override with user settings defaults.update(kwargs) @@ -214,15 +207,15 @@ def client(**kwargs): # default values every scenario should define at a minimum _minimum_defaults_template = { - 'key_vals': {}, - 'global_vars': {}, + "key_vals": {}, + "global_vars": {}, } _scen_defaults_template = { - 'recv_timeout': 5000, - 'call_count': 1, - 'rate': 1, - 'limit': 1, - 'logdir': tempfile.gettempdir(), + "recv_timeout": 5000, + "call_count": 1, + "rate": 1, + "limit": 1, + "logdir": tempfile.gettempdir(), } _scen_defaults_template.update(deepcopy(_minimum_defaults_template)) @@ -233,7 +226,7 @@ def Scenario(agents, **kwargs): If called it will invoke the standard run hooks. """ - scentype = type('Scenario', (ScenarioType,), {}) + scentype = type("Scenario", (ScenarioType,), {}) _defs = OrderedDict(deepcopy(_scen_defaults_template)) # for any passed kwargs that have keys in ``_defaults_template``, set them @@ -244,7 +237,7 @@ def Scenario(agents, **kwargs): # if a `defaults` kwarg is passed in by the user override template values with # values from that as well - user_defaults = kwargs.pop('defaults', None) + user_defaults = kwargs.pop("defaults", None) if user_defaults: _defs.update(user_defaults) @@ -261,8 +254,13 @@ class ScenarioType(object): """ def __init__( - self, agents, defaults, clientdefaults=None, - serverdefaults=None, confpy=None, enable_screen_file=True + self, + agents, + defaults, + clientdefaults=None, + serverdefaults=None, + confpy=None, + enable_screen_file=True, ): # placeholder for process "runner" self._runner = None @@ -278,12 +276,14 @@ def __init__( # client settings self._clientdefaults = OrderedDict( - clientdefaults or deepcopy(_minimum_defaults_template)) + clientdefaults or deepcopy(_minimum_defaults_template) + ) self.clientdefaults = utils.DictProxy(self._clientdefaults, ua_attrs)() # server settings self._serverdefaults = OrderedDict( - serverdefaults or deepcopy(_minimum_defaults_template)) + serverdefaults or deepcopy(_minimum_defaults_template) + ) self.serverdefaults = utils.DictProxy(self._serverdefaults, ua_attrs)() # hook module @@ -296,15 +296,11 @@ def agents(self): @property def clients(self): - return OrderedDict( - (ua.name, ua) for ua in self._agents if ua.is_client() - ) + return OrderedDict((ua.name, ua) for ua in self._agents if ua.is_client()) @property def servers(self): - return OrderedDict( - (ua.name, ua) for ua in self._agents if ua.is_server() - ) + return OrderedDict((ua.name, ua) for ua in self._agents if ua.is_server()) @property def name(self): @@ -320,11 +316,11 @@ def name(self): # concat dirnames if scripts come from separate dir locations if len(set(dirnames)) > 1: - return '_'.join(dirnames) + return "_".join(dirnames) return dirnames[0] - def findbyaddr(self, socket, bytype=''): + def findbyaddr(self, socket, bytype=""): """Lookup an agent by socket address. `bytype` is a keyword which determines which socket to use at the key and can be one of {'media', 'dest', 'src'} @@ -336,8 +332,7 @@ def findbyaddr(self, socket, bytype=''): @property def has_media(self): - """Bool dermining whether this scen is a media player - """ + """Bool dermining whether this scen is a media player""" if any(agent.plays_media for agent in self._agents): return True return False @@ -351,14 +346,12 @@ def dirpath(self): return path.dirname(scenfile) if scenfile else None def cmditems(self): - """Agent names to cmd strings items - """ + """Agent names to cmd strings items""" return [(agent.name, agent.cmd) for agent in self.prepare()] def pformat_cmds(self): - """Pretty format string for printing agent commands - """ - return '\n\n'.join( + """Pretty format string for printing agent commands""" + return "\n\n".join( ["{}:\n{}".format(name, cmd) for name, cmd in self.cmditems()] ) @@ -382,10 +375,10 @@ def merge(dicts): if agent.is_client(): secondary = self._clientdefaults - dname = 'clientdefaults' + dname = "clientdefaults" elif agent.is_server(): secondary = self._serverdefaults - dname = 'serverdefaults' + dname = "serverdefaults" else: secondary = {} dname = "unspecialized ua" @@ -395,7 +388,7 @@ def merge(dicts): # apply defaults ordered = [self._defaults, secondary, agent.todict()] - for name, defs in zip(['defaults', dname, 'agent.todict()'], ordered): + for name, defs in zip(["defaults", dname, "agent.todict()"], ordered): log.debug("{} '{}' contents:\n{}".format(agent.name, name, defs)) params = merge(ordered) @@ -425,11 +418,12 @@ def from_settings(self, **kwargs): all normal plugin hooks. """ from . import scenario + scenkwargs = { - 'dirpath': self.dirpath, - 'defaults': self._defaults.copy(), - 'clientdefaults': self._clientdefaults.copy(), - 'serverdefaults': self._serverdefaults.copy(), + "dirpath": self.dirpath, + "defaults": self._defaults.copy(), + "clientdefaults": self._clientdefaults.copy(), + "serverdefaults": self._serverdefaults.copy(), } for key, value in kwargs.items(): if key in scenkwargs: @@ -438,10 +432,8 @@ def from_settings(self, **kwargs): return scenario(**scenkwargs) def from_agents(self, agents=None, autolocalsocks=True, **scenkwargs): - """Create a new scenario from prepared agents. - """ - return type(self)( - self.prepare(agents), self._defaults, confpy=self.mod) + """Create a new scenario from prepared agents.""" + return type(self)(self.prepare(agents), self._defaults, confpy=self.mod) async def arun( self, @@ -467,19 +459,9 @@ def finalize(self, *, timeout=DEFAULT_RUNNER_TIMEOUT): ) ) - def run( - self, - timeout=180, - **kwargs - ): + def run(self, timeout=180, **kwargs): """Run scenario blocking to completion.""" - return trio.run( - partial( - self.arun, - timeout=timeout, - **kwargs - ) - ) + return trio.run(partial(self.arun, timeout=timeout, **kwargs)) def __call__(self, *args, **kwargs): # TODO: deprecation warning here diff --git a/pysipp/cli/minidom.py b/pysipp/cli/minidom.py index 9118828..1304d44 100644 --- a/pysipp/cli/minidom.py +++ b/pysipp/cli/minidom.py @@ -11,7 +11,7 @@ class AttributeSorter(object): attributes bubble to the front. """ - __special__ = ('request', 'response') + __special__ = ("request", "response") def __init__(self, obj): self.obj = obj @@ -54,7 +54,7 @@ def monkeypatch_scenario_xml(self, writer, indent="", addindent="", newl=""): It needs to be there or SIPp otherwise seems to have trouble parsing the document. """ - writer.write('\n') + writer.write("\n") minidom.Element.writexml(self, writer, indent, addindent, newl) @@ -71,18 +71,17 @@ def monkeypatch_element_xml(self, writer, indent="", addindent="", newl=""): a_names = sorted(attrs.keys(), key=AttributeSorter) for a_name in a_names: - writer.write(" {}=\"".format(a_name)) + writer.write(' {}="'.format(a_name)) minidom._write_data(writer, attrs[a_name].value) - writer.write("\"") + writer.write('"') if self.childNodes: writer.write(">") - if (len(self.childNodes) == 1 and - self.childNodes[0].nodeType == Node.TEXT_NODE): - self.childNodes[0].writexml(writer, '', '', '') + if len(self.childNodes) == 1 and self.childNodes[0].nodeType == Node.TEXT_NODE: + self.childNodes[0].writexml(writer, "", "", "") else: writer.write(newl) for node in self.childNodes: - node.writexml(writer, indent+addindent, addindent, newl) + node.writexml(writer, indent + addindent, addindent, newl) writer.write(indent) writer.write("{}".format(self.tagName, newl)) else: diff --git a/pysipp/cli/sippfmt.py b/pysipp/cli/sippfmt.py index 692478a..3a1b00c 100644 --- a/pysipp/cli/sippfmt.py +++ b/pysipp/cli/sippfmt.py @@ -76,16 +76,16 @@ def process_document(filepath): structure of the XML document rather than its content. """ dom = minidom.parse(filepath) - scenario = next(elem for elem in dom.childNodes - if getattr(elem, 'tagName', None) == 'scenario') + scenario = next( + elem for elem in dom.childNodes if getattr(elem, "tagName", None) == "scenario" + ) - imp = minidom.getDOMImplementation('') - dt = imp.createDocumentType('scenario', None, 'sipp.dtd') - doc = imp.createDocument(None, 'scenario', dt) + imp = minidom.getDOMImplementation("") + dt = imp.createDocumentType("scenario", None, "sipp.dtd") + doc = imp.createDocument(None, "scenario", dt) new_scen = doc.childNodes[-1] - new_scen.writexml = types.MethodType(minidom.monkeypatch_scenario_xml, - new_scen) + new_scen.writexml = types.MethodType(minidom.monkeypatch_scenario_xml, new_scen) for elem in scenario.childNodes: if elem.nodeType == minidom.Node.TEXT_NODE: @@ -101,8 +101,7 @@ def process_document(filepath): new_scen.appendChild(elem) # delete the last separator - if (new_scen.childNodes - and isinstance(new_scen.childNodes[-1], minidom.Newline)): + if new_scen.childNodes and isinstance(new_scen.childNodes[-1], minidom.Newline): del new_scen.childNodes[-1] doc.appendChild(new_scen) @@ -111,14 +110,14 @@ def process_document(filepath): def main(): """Format sipp scripts.""" - parser = argparse.ArgumentParser(description='Format sipp scripts') - parser.add_argument('filename') + parser = argparse.ArgumentParser(description="Format sipp scripts") + parser.add_argument("filename") args = parser.parse_args() doc = process_document(args.filename) - xml = doc.toprettyxml(indent=' ', encoding='ISO-8859-1') - print(xml, end='') + xml = doc.toprettyxml(indent=" ", encoding="ISO-8859-1") + print(xml, end="") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/pysipp/command.py b/pysipp/command.py index 6b87066..d4da8c8 100644 --- a/pysipp/command.py +++ b/pysipp/command.py @@ -1,6 +1,6 @@ -''' +""" Command string rendering -''' +""" import string import socket from collections import OrderedDict @@ -24,7 +24,8 @@ def __get__(self, obj, cls): if obj is None: return self return obj._values.setdefault( - self.name, self._default() if self._default else None, + self.name, + self._default() if self._default else None, ) def __set__(self, obj, value): @@ -32,8 +33,7 @@ def __set__(self, obj, value): obj._values[self.name] = value def render(self, value): - return self.fmtstr.format( - **{self.name: "'{}'".format(value)}) if value else '' + return self.fmtstr.format(**{self.name: "'{}'".format(value)}) if value else "" class AddrField(Field): @@ -58,14 +58,14 @@ def __set__(self, obj, value): def render(self, value): # return the fmt string with a null string replacement - return self.fmtstr.format(**{self.name: ''}) if value else '' + return self.fmtstr.format(**{self.name: ""}) if value else "" class DictField(Field): _default = OrderedDict def render(self, value): - return ''.join( + return "".join( self.fmtstr.format(**{self.name: "{} '{}'".format(key, val)}) for key, val in value.items() ) @@ -75,14 +75,13 @@ class ListField(Field): _default = [] def render(self, value): - return ''.join( - self.fmtstr.format(**{self.name: "'{}'".format(val)}) - for val in value + return "".join( + self.fmtstr.format(**{self.name: "'{}'".format(val)}) for val in value ) def cmdstrtype(spec): - '''Build a command str renderer from an iterable of format string tokens. + """Build a command str renderer from an iterable of format string tokens. Given a `spec` (i.e. an iterable of format string specifiers), this function returns a command string renderer type which allows for @@ -110,7 +109,8 @@ def cmdstrtype(spec): >>> cmd.remote_port = 5060 >>> cmd.render() '/usr/bin/sipp doggy.com:5060 -i 192.168.0.1' - ''' + """ + class Renderer(object): _specparams = OrderedDict() @@ -131,19 +131,19 @@ def render(self): if value is not None: tokens.append(descr.render(value)) - return ''.join(tokens) + return "".join(tokens) def __setattr__(self, key, value): # immutable after instantiation - if getattr( - self, '_init', False - ) and ( - key not in self.__class__.__dict__ - ) and ( - key not in self._specparams - ) and key[0] is not '_': + if ( + getattr(self, "_init", False) + and (key not in self.__class__.__dict__) + and (key not in self._specparams) + and key[0] is not "_" + ): raise AttributeError( - "no settable public attribute '{}' defined".format(key)) + "no settable public attribute '{}' defined".format(key) + ) object.__setattr__(self, key, value) @classmethod @@ -155,14 +155,12 @@ def keys(cls): return [key for key, descr in cls.descriptoritems()] def applydict(self, d): - """Apply contents of dict `d` onto local instance variables. - """ + """Apply contents of dict `d` onto local instance variables.""" for name, value in d.items(): setattr(self, name, value) def todict(self): - """Serialze all descriptor defined attributes into a dictionary - """ + """Serialze all descriptor defined attributes into a dictionary""" contents = {} for key in self.keys(): val = getattr(self, key) @@ -186,65 +184,65 @@ def todict(self): sipp_spec = [ # contact info - '{prefix} ', - '{bin_path} ', - ('-i {local_host} ', AddrField), - '-p {local_port} ', - '-s {uri_username} ', - ('-rsa {proxy_host}', AddrField), # NOTE: no space - ':{proxy_port} ', - '-auth_uri {auth_uri} ', + "{prefix} ", + "{bin_path} ", + ("-i {local_host} ", AddrField), + "-p {local_port} ", + "-s {uri_username} ", + ("-rsa {proxy_host}", AddrField), # NOTE: no space + ":{proxy_port} ", + "-auth_uri {auth_uri} ", # sockets and protocols - '-bind_local {bind_local} ', - ('-mi {media_addr} ', AddrField), - '-mp {media_port} ', - '-t {transport} ', + "-bind_local {bind_local} ", + ("-mi {media_addr} ", AddrField), + "-mp {media_port} ", + "-t {transport} ", # scenario config/ctl - '-sn {scen_name} ', - '-sf {scen_file} ', - '-oocsf {ooc_scen_file} ', - '-recv_timeout {recv_timeout} ', - '-timeout {timeout} ', - '-d {pause_duration} ', - '-default_behaviors {default_behaviors} ', - ('-3pcc {ipc_host}', AddrField), # NOTE: no space - ':{ipc_port} ', + "-sn {scen_name} ", + "-sf {scen_file} ", + "-oocsf {ooc_scen_file} ", + "-recv_timeout {recv_timeout} ", + "-timeout {timeout} ", + "-d {pause_duration} ", + "-default_behaviors {default_behaviors} ", + ("-3pcc {ipc_host}", AddrField), # NOTE: no space + ":{ipc_port} ", # SIP vars - '-cid_str {cid_str} ', - '-base_cseq {base_cseq} ', - '-au {auth_username} ', - '-ap {auth_password} ', + "-cid_str {cid_str} ", + "-base_cseq {base_cseq} ", + "-au {auth_username} ", + "-ap {auth_password} ", # load settings - '-r {rate} ', - '-l {limit} ', - '-m {call_count} ', - '-rp {rate_period} ', - '-users {users} ', - '-deadcall_wait {deadcall_wait} ', + "-r {rate} ", + "-l {limit} ", + "-m {call_count} ", + "-rp {rate_period} ", + "-users {users} ", + "-deadcall_wait {deadcall_wait} ", # data insertion - ('-key {key_vals} ', DictField), - ('-set {global_vars} ', DictField), + ("-key {key_vals} ", DictField), + ("-set {global_vars} ", DictField), # files - '-error_file {error_file} ', - '-calldebug_file {calldebug_file} ', - '-message_file {message_file} ', - '-log_file {log_file} ', - '-inf {info_file} ', - ('-inf {info_files} ', ListField), - '-screen_file {screen_file} ', + "-error_file {error_file} ", + "-calldebug_file {calldebug_file} ", + "-message_file {message_file} ", + "-log_file {log_file} ", + "-inf {info_file} ", + ("-inf {info_files} ", ListField), + "-screen_file {screen_file} ", # bool flags - ('-rtp_echo {rtp_echo}', BoolField), - ('-timeout_error {timeout_error}', BoolField), - ('-aa {auto_answer}', BoolField), - ('-trace_err {trace_error}', BoolField), - ('-trace_calldebug {trace_calldebug}', BoolField), - ('-trace_error_codes {trace_error_codes}', BoolField), - ('-trace_msg {trace_message}', BoolField), - ('-trace_logs {trace_log}', BoolField), - ('-trace_screen {trace_screen}', BoolField), - ('-error_overwrite {error_overwrite}', BoolField), - ('{remote_host}', AddrField), # NOTE: no space - ':{remote_port}', + ("-rtp_echo {rtp_echo}", BoolField), + ("-timeout_error {timeout_error}", BoolField), + ("-aa {auto_answer}", BoolField), + ("-trace_err {trace_error}", BoolField), + ("-trace_calldebug {trace_calldebug}", BoolField), + ("-trace_error_codes {trace_error_codes}", BoolField), + ("-trace_msg {trace_message}", BoolField), + ("-trace_logs {trace_log}", BoolField), + ("-trace_screen {trace_screen}", BoolField), + ("-error_overwrite {error_overwrite}", BoolField), + ("{remote_host}", AddrField), # NOTE: no space + ":{remote_port}", ] diff --git a/pysipp/hookspec.py b/pysipp/hookspec.py index fa953c0..ddec3a5 100644 --- a/pysipp/hookspec.py +++ b/pysipp/hookspec.py @@ -1,9 +1,9 @@ -''' +""" hookspec defs -''' +""" import pluggy -hookspec = pluggy.HookspecMarker('pysipp') +hookspec = pluggy.HookspecMarker("pysipp") # UA factory hooks @@ -44,8 +44,7 @@ def pysipp_conf_scen_protocol(agents, confpy, scenkwargs): @hookspec(firstresult=True) def pysipp_order_agents(agents, clients, servers): - """Return ua iterator which delivers agents in launch order - """ + """Return ua iterator which delivers agents in launch order""" @hookspec(firstresult=True) diff --git a/pysipp/launch.py b/pysipp/launch.py index 0a922dd..ff74ce8 100644 --- a/pysipp/launch.py +++ b/pysipp/launch.py @@ -23,8 +23,7 @@ class TimeoutError(Exception): class SIPpFailure(RuntimeError): - """SIPp commands failed - """ + """SIPp commands failed""" class TrioRunner(object): @@ -32,46 +31,37 @@ class TrioRunner(object): with a non-zero exit code, immediately canacel all remaining processes and collect std streams. """ + def __init__( self, ): # store proc results self._procs = OrderedDict() - async def run( - self, - cmds, - rate=300, - **kwargs - ): + async def run(self, cmds, rate=300, **kwargs): if self.is_alive(): - raise RuntimeError( - "Not all processes from a prior run have completed" - ) + raise RuntimeError("Not all processes from a prior run have completed") if self._procs: raise RuntimeError( "Process results have not been cleared from previous run" ) # run agent commands in sequence for cmd in cmds: - log.debug( - "launching cmd:\n\"{}\"\n".format(cmd)) + log.debug('launching cmd:\n"{}"\n'.format(cmd)) proc = await trio.open_process( - shlex.split(cmd), - stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE + shlex.split(cmd), stdout=subprocess.DEVNULL, stderr=subprocess.PIPE ) self._procs[cmd] = proc # limit launch rate - time.sleep(1. / rate) + time.sleep(1.0 / rate) return self._procs async def get(self, timeout=180): - '''Block up to `timeout` seconds for all agents to complete. + """Block up to `timeout` seconds for all agents to complete. Either return (cmd, proc) pairs or raise `TimeoutError` on timeout - ''' + """ signalled = None # taken mostly verbatim from ``trio.run_process()`` @@ -115,43 +105,46 @@ async def wait_on_proc(proc): # all procs were killed by SIGUSR1 raise TimeoutError( "pids '{}' failed to complete after '{}' seconds".format( - pformat([p.pid for p in signalled.values()]), timeout) + pformat([p.pid for p in signalled.values()]), timeout + ) ) def iterprocs(self): - '''Iterate all processes which are still alive yielding + """Iterate all processes which are still alive yielding (cmd, proc) pairs - ''' - return ((cmd, proc) for cmd, proc in self._procs.items() - if proc and proc.poll() is None) + """ + return ( + (cmd, proc) + for cmd, proc in self._procs.items() + if proc and proc.poll() is None + ) def stop(self): - '''Stop all agents with SIGUSR1 as per SIPp's signal handling - ''' + """Stop all agents with SIGUSR1 as per SIPp's signal handling""" return self._signalall(signal.SIGUSR1) def terminate(self): - '''Kill all agents with SIGTERM - ''' + """Kill all agents with SIGTERM""" return self._signalall(signal.SIGTERM) def _signalall(self, signum): signalled = OrderedDict() for cmd, proc in self.iterprocs(): proc.send_signal(signum) - log.warn("sent signal '{}' to cmd '{}' with pid '{}'" - .format(signum, cmd, proc.pid)) + log.warn( + "sent signal '{}' to cmd '{}' with pid '{}'".format( + signum, cmd, proc.pid + ) + ) signalled[cmd] = proc return signalled def is_alive(self): - '''Return bool indicating whether some agents are still alive - ''' + """Return bool indicating whether some agents are still alive""" return any(self.iterprocs()) def clear(self): - '''Clear all processes from the last run - ''' + """Clear all processes from the last run""" assert not self.is_alive(), "Not all processes have completed" self._procs.clear() diff --git a/pysipp/load.py b/pysipp/load.py index 9fd7d65..cf95208 100644 --- a/pysipp/load.py +++ b/pysipp/load.py @@ -4,36 +4,33 @@ import glob import os from . import utils + log = utils.get_logger() class CollectionError(Exception): - """Scenario dir collection error - """ + """Scenario dir collection error""" def glob_for_scripts(directory): - '''Find scenario xml scripts and conf.py in script dir - ''' - xmls = glob.iglob(directory + '/*.xml') + """Find scenario xml scripts and conf.py in script dir""" + xmls = glob.iglob(directory + "/*.xml") # double check - xmls = [f for f in xmls if 'xml' in os.path.splitext(f)[1]] - confpy = glob.glob(directory + '/pysipp_conf.py') + xmls = [f for f in xmls if "xml" in os.path.splitext(f)[1]] + confpy = glob.glob(directory + "/pysipp_conf.py") if len(confpy) > 1: - raise ValueError( - 'can only be at most one pysipp_conf.py in scen directory!' - ) - log.debug("discovered xmls:\n{}".format('\n'.join(xmls))) + raise ValueError("can only be at most one pysipp_conf.py in scen directory!") + log.debug("discovered xmls:\n{}".format("\n".join(xmls))) return xmls, confpy[0] if confpy else None def iter_scen_dirs(rootdir, dir_filter=lambda dir_name: dir_name): - '''Build a map of SIPp scripts by searching the filesystem for .xml files + """Build a map of SIPp scripts by searching the filesystem for .xml files :param str rootdir: dir in the filesystem to start scanning for xml files :return: an iterator over all scenario dirs yielding tuples of the form (, , ) - ''' + """ mod_space = set() for path, dirnames, filenames in os.walk(rootdir): @@ -52,11 +49,15 @@ def iter_scen_dirs(rootdir, dir_filter=lambda dir_name: dir_name): log.debug("No pysipp_conf.py found under '{}'".format(path)) # load module sources - mod = utils.load_mod( - confpy, - # use unique names (as far as scendirs go) to avoid module caching - name='pysipp_confpy_{}'.format(os.path.dirname(confpy)) - ) if confpy else None + mod = ( + utils.load_mod( + confpy, + # use unique names (as far as scendirs go) to avoid module caching + name="pysipp_confpy_{}".format(os.path.dirname(confpy)), + ) + if confpy + else None + ) # verify confpy mods should be unique if mod: diff --git a/pysipp/netplug.py b/pysipp/netplug.py index fa3dffa..80dd61e 100644 --- a/pysipp/netplug.py +++ b/pysipp/netplug.py @@ -15,7 +15,12 @@ def getsockaddr(host, family=socket.AF_INET, port=0, sockmod=socket): other processes acquiring the addr before our SIPp process re-binds. """ for fam, stype, proto, _, sa in socket.getaddrinfo( - host, port, family, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE, + host, + port, + family, + socket.SOCK_DGRAM, + 0, + socket.AI_PASSIVE, ): s = socket.socket(family, stype, proto) s.bind(sa) diff --git a/pysipp/plugin.py b/pysipp/plugin.py index 2d61ddc..cd88b92 100644 --- a/pysipp/plugin.py +++ b/pysipp/plugin.py @@ -1,19 +1,18 @@ -''' +""" `pluggy` plugin and hook management -''' +""" import pluggy import contextlib from . import hookspec -hookimpl = pluggy.HookimplMarker('pysipp') -mng = pluggy.PluginManager('pysipp', implprefix='pysipp') +hookimpl = pluggy.HookimplMarker("pysipp") +mng = pluggy.PluginManager("pysipp", implprefix="pysipp") mng.add_hookspecs(hookspec) @contextlib.contextmanager def register(plugins): - """Temporarily register plugins - """ + """Temporarily register plugins""" try: if any(plugins): for p in plugins: diff --git a/pysipp/report.py b/pysipp/report.py index b4dbb49..0dac395 100644 --- a/pysipp/report.py +++ b/pysipp/report.py @@ -1,6 +1,6 @@ -''' +""" reporting for writing SIPp log files to the console -''' +""" import time from os import path from collections import OrderedDict @@ -35,22 +35,24 @@ def err_summary(agents2procs): if any(name2ec.values()): # raise a detailed error msg = "Some agents failed\n" - msg += '\n'.join("'{}' with exit code {} -> {}".format( - name, rc, EXITCODES.get(rc, "unknown exit code")) + msg += "\n".join( + "'{}' with exit code {} -> {}".format( + name, rc, EXITCODES.get(rc, "unknown exit code") + ) for name, rc in name2ec.items() ) return msg -async def emit_logfiles(agents2procs, level='warn', max_lines=100): - """Log all available SIPp log-file contents - """ +async def emit_logfiles(agents2procs, level="warn", max_lines=100): + """Log all available SIPp log-file contents""" emit = getattr(log, level) for ua, proc in agents2procs: # print stderr - emit("stderr for '{}' @ {}\n{}\n".format( - ua.name, ua.srcaddr, proc.stderr_output)) + emit( + "stderr for '{}' @ {}\n{}\n".format(ua.name, ua.srcaddr, proc.stderr_output) + ) # FIXME: no idea, but some logs are not being printed without this # logging mod bug? time.sleep(0.01) @@ -58,7 +60,7 @@ async def emit_logfiles(agents2procs, level='warn', max_lines=100): # print log file contents for name, fpath in ua.iter_toconsole_items(): if fpath and path.isfile(fpath): - with open(fpath, 'r') as lf: + with open(fpath, "r") as lf: lines = lf.readlines() llen = len(lines) @@ -68,11 +70,14 @@ async def emit_logfiles(agents2procs, level='warn', max_lines=100): "...\nOutput has been truncated to {} lines - " "see '{}' for full details\n" ).format(max_lines, fpath) - output = ''.join(lines[:max_lines]) + toolong + output = "".join(lines[:max_lines]) + toolong else: - output = ''.join(lines) + output = "".join(lines) # log it - emit("'{}' contents for '{}' @ {}:\n{}".format( - name, ua.name, ua.srcaddr, output)) + emit( + "'{}' contents for '{}' @ {}:\n{}".format( + name, ua.name, ua.srcaddr, output + ) + ) # FIXME: same as above time.sleep(0.01) diff --git a/pysipp/utils.py b/pysipp/utils.py index ca6bbfe..098fbc5 100644 --- a/pysipp/utils.py +++ b/pysipp/utils.py @@ -6,32 +6,30 @@ LOG_FORMAT = ( "%(asctime)s %(threadName)s [%(levelname)s] %(name)s " - "%(filename)s:%(lineno)d : %(message)s") + "%(filename)s:%(lineno)d : %(message)s" +) -DATE_FORMAT = '%b %d %H:%M:%S' +DATE_FORMAT = "%b %d %H:%M:%S" def get_logger(): - """Get the project logger instance - """ - return logging.getLogger('pysipp') + """Get the project logger instance""" + return logging.getLogger("pysipp") def log_to_stderr(level="INFO", **kwargs): - defaults = {'format': LOG_FORMAT, 'level': level} + defaults = {"format": LOG_FORMAT, "level": level} defaults.update(kwargs) logging.basicConfig(**defaults) def get_tmpdir(): - """Return a random temp dir - """ - return tempfile.mkdtemp(prefix='pysipp_') + """Return a random temp dir""" + return tempfile.mkdtemp(prefix="pysipp_") def load_mod(path, name=None): - """Load a source file as a module - """ + """Load a source file as a module""" name = name or os.path.splitext(os.path.basename(path))[0] # load module sources return imp.load_source(name, path) @@ -44,7 +42,7 @@ def iter_data_descrs(cls): for name in dir(cls): attr = getattr(cls, name) if inspect.isdatadescriptor(attr): - if (hasattr(attr, 'fset') and not attr.fset) or '_' in name[0]: + if (hasattr(attr, "fset") and not attr.fset) or "_" in name[0]: continue yield name, attr @@ -53,10 +51,12 @@ def DictProxy(d, keys, cls=None): """A dictionary proxy object which provides attribute access to the elements of the provided dictionary `d` """ + class DictProxyAttr(object): """An attribute which when modified proxies to an instance dictionary named `dictname`. """ + def __init__(self, key): self.key = key @@ -78,14 +78,14 @@ def __set__(self, obj, value): else: # delegate some methods to the original dict proxied_attrs = [ - '__repr__', - '__getitem__', - '__setitem__', - '__contains__', - '__len__', - 'get', - 'update', - 'setdefault', + "__repr__", + "__getitem__", + "__setitem__", + "__contains__", + "__len__", + "get", + "update", + "setdefault", ] attrs.update({attr: getattr(d, attr) for attr in proxied_attrs}) @@ -93,7 +93,7 @@ def __set__(self, obj, value): def init(self): self.__dict__ = d - attrs.update({'__init__': init}) + attrs.update({"__init__": init}) # render a new type - return type('DictProxy', (), attrs) + return type("DictProxy", (), attrs) diff --git a/tests/conftest.py b/tests/conftest.py index a28aece..b1cc90c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,6 @@ -''' +""" unit testing -''' +""" import pytest import os from pysipp import agent, scenario, utils @@ -22,16 +22,12 @@ def scendir(): @pytest.fixture def default_agents(): - uas = agent.server(local_host='127.0.0.1', local_port=5060, call_count=1) + uas = agent.server(local_host="127.0.0.1", local_port=5060, call_count=1) uac = agent.client(call_count=1, destaddr=(uas.local_host, uas.local_port)) return uas, uac -@pytest.fixture( - params=[True, False], - ids="autolocalsocks={}".format -) +@pytest.fixture(params=[True, False], ids="autolocalsocks={}".format) def basic_scen(request): - """The most basic scenario instance - """ + """The most basic scenario instance""" return scenario(autolocalsocks=request.param) diff --git a/tests/scens/default_with_confpy/pysipp_conf.py b/tests/scens/default_with_confpy/pysipp_conf.py index d662d31..4c63525 100644 --- a/tests/scens/default_with_confpy/pysipp_conf.py +++ b/tests/scens/default_with_confpy/pysipp_conf.py @@ -1,6 +1,6 @@ def pysipp_conf_scen(agents, scen): - scen.uri_username = 'doggy' - agents['uac'].srcaddr = '127.0.0.1', 5070 + scen.uri_username = "doggy" + agents["uac"].srcaddr = "127.0.0.1", 5070 def pysipp_order_agents(agents, clients, servers): diff --git a/tests/test_agent.py b/tests/test_agent.py index 4e0d2af..2f49760 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -1,6 +1,6 @@ -''' +""" pysipp.agent module tests -''' +""" import pytest import tempfile import pysipp @@ -13,9 +13,8 @@ def ua(): def test_ua(ua): - """Set up a typeless agent and perform basic attr checks - """ - sock = ('10.10.9.9', 5060) + """Set up a typeless agent and perform basic attr checks""" + sock = ("10.10.9.9", 5060) ua.proxyaddr = sock assert ua.name == str(None) assert "'{}':'{}'".format(*sock) in ua.render() @@ -39,7 +38,7 @@ def check_log_files(ua, logdir=None): @pytest.mark.parametrize( "funcname", - ['ua', 'client', 'server'], + ["ua", "client", "server"], ) def test_logdir(funcname): """Verify that a default logdir is set and filenames are @@ -52,24 +51,22 @@ def test_logdir(funcname): def test_scen_assign_logdir(): - """Verify log file arguments when logdir is set using Scenario.defaults - """ + """Verify log file arguments when logdir is set using Scenario.defaults""" scen = pysipp.scenario() - logdir = tempfile.mkdtemp(suffix='_pysipp') + logdir = tempfile.mkdtemp(suffix="_pysipp") scen.defaults.logdir = logdir for ua in scen.prepare(): check_log_files(ua, logdir) def test_scen_pass_logdir(): - """Verify log file arguments when logdir is set using Scenario.defaults - """ - logdir = tempfile.mkdtemp(suffix='_pysipp') + """Verify log file arguments when logdir is set using Scenario.defaults""" + logdir = tempfile.mkdtemp(suffix="_pysipp") scen = pysipp.scenario(logdir=logdir) assert scen.defaults.logdir == logdir # logdir isn't set until the scenario is "prepared" - assert scen.agents['uac'].logdir is None + assert scen.agents["uac"].logdir is None # logdir is set once scenario is "rendered" for ua in scen.prepare(): @@ -77,9 +74,8 @@ def test_scen_pass_logdir(): def test_walk_pass_logdir(): - logdir = tempfile.mkdtemp(suffix='_pysipp') - scen = next(pysipp.walk( - './tests/scens/default/', logdir=logdir))[1] + logdir = tempfile.mkdtemp(suffix="_pysipp") + scen = next(pysipp.walk("./tests/scens/default/", logdir=logdir))[1] assert scen.logdir == logdir # logdir is set once scenario is "rendered" @@ -89,14 +85,14 @@ def test_walk_pass_logdir(): def test_client(): # check the built-in uac xml scenario - remote_sock = ('192.168.1.1', 5060) + remote_sock = ("192.168.1.1", 5060) uac = agent.client(destaddr=remote_sock) cmdstr = uac.render() assert "-sn 'uac'" in cmdstr assert "'{}':'{}'".format(*remote_sock) in cmdstr # pretend script file - script = '/home/sipp_scen/uac.xml' + script = "/home/sipp_scen/uac.xml" uac2 = agent.client(destaddr=remote_sock, scen_file=script) cmdstr = uac2.render() assert "-sn 'uac'" not in cmdstr @@ -110,20 +106,20 @@ def test_server(): assert not (ua.remote_host and ua.remote_port) -@pytest.mark.parametrize('ua, retcode, kwargs, exc', [ - # test unspecialized ua failure - (agent.ua(), 255, {}, RuntimeError), - - # test client failure on bad remote destination - (agent.client(destaddr=('99.99.99.99', 5060)), 1, {}, RuntimeError), - - # test if server times out it is signalled - (agent.server(), -9, {'timeout': 1}, launch.TimeoutError)], - ids=['ua', 'uac', 'uas'], +@pytest.mark.parametrize( + "ua, retcode, kwargs, exc", + [ + # test unspecialized ua failure + (agent.ua(), 255, {}, RuntimeError), + # test client failure on bad remote destination + (agent.client(destaddr=("99.99.99.99", 5060)), 1, {}, RuntimeError), + # test if server times out it is signalled + (agent.server(), -9, {"timeout": 1}, launch.TimeoutError), + ], + ids=["ua", "uac", "uas"], ) def test_failures(ua, retcode, kwargs, exc): - """Test failure cases for all types of agents - """ + """Test failure cases for all types of agents""" runner = launch.TrioRunner() # run it without raising @@ -138,7 +134,8 @@ def test_failures(ua, retcode, kwargs, exc): assert len(list(runner.iterprocs())) == 0 # tests transparency of the defaults config pipeline scen = plugin.mng.hook.pysipp_conf_scen_protocol( - agents=[ua], confpy=None, scenkwargs={}) + agents=[ua], confpy=None, scenkwargs={} + ) cmd = scen.prepare_agent(ua).render() assert cmd in cmds2procs assert len(cmds2procs) == 1 @@ -171,7 +168,7 @@ def test_scenario(): assert uac is list(scen.clients.values())[0] # ensure defaults attr setting works - doggy = 'doggy' + doggy = "doggy" scen.local_host = doggy uas, uac = scen.prepare() assert uac.local_host == uas.local_host == doggy @@ -183,7 +180,7 @@ def test_scenario(): # same error for any non-spec defined agent attr with pytest.raises(AttributeError): - scen.agentdefaults.local_man = 'flasher' + scen.agentdefaults.local_man = "flasher" scen.prepare() # defaults on servers only @@ -192,15 +189,16 @@ def test_scenario(): assert uas.uri_username == doggy assert uac.uri_username != doggy - assert scen.name == 'uas_uac' + assert scen.name == "uas_uac" def test_pass_bad_socket_addr(): with pytest.raises(ValueError): - pysipp.client(proxyaddr='10.10.8.88') + pysipp.client(proxyaddr="10.10.8.88") + def test_authentication_arguments(): - client = agent.client(auth_username='username', auth_password='passw0rd') + client = agent.client(auth_username="username", auth_password="passw0rd") cmd = client.render() diff --git a/tests/test_commands.py b/tests/test_commands.py index 70e1aee..239cd69 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,6 +1,6 @@ -''' +""" Command generation -''' +""" import pytest from pysipp.command import SippCmd from pysipp import utils @@ -11,11 +11,11 @@ def test_bool_field(): cmd = SippCmd() with pytest.raises(ValueError): - cmd.rtp_echo = 'doggy' + cmd.rtp_echo = "doggy" - assert '-rtp_echo' not in cmd.render() + assert "-rtp_echo" not in cmd.render() cmd.rtp_echo = True - assert '-rtp_echo' in cmd.render() + assert "-rtp_echo" in cmd.render() assert type(cmd).rtp_echo is not cmd.rtp_echo @@ -24,35 +24,35 @@ def test_dict_field(): assert isinstance(cmd.key_vals, dict) # one entry - cmd.key_vals['doggy'] = 100 + cmd.key_vals["doggy"] = 100 assert "-key doggy '100'" in cmd.render() # two entries - cmd.key_vals['kitty'] = 200 + cmd.key_vals["kitty"] = 200 assert "-key kitty '200'" in cmd.render() - assert "-key kitty '200'" in cmd.render() and\ - "-key doggy '100'" in cmd.render() + assert "-key kitty '200'" in cmd.render() and "-key doggy '100'" in cmd.render() # three entries - cmd.key_vals['mousey'] = 300 - assert "-key kitty '200'" in cmd.render() and\ - "-key doggy '100'" in cmd.render() and\ - "-key mousey '300'" in cmd.render() + cmd.key_vals["mousey"] = 300 + assert ( + "-key kitty '200'" in cmd.render() + and "-key doggy '100'" in cmd.render() + and "-key mousey '300'" in cmd.render() + ) log.debug("cmd is '{}'".format(cmd.render())) # override entire dict cmd.key_vals = { - 'mousey': 300, - 'doggy': 100, + "mousey": 300, + "doggy": 100, } assert "-key kitty '200'" not in cmd.render() - assert "-key doggy '100'" in cmd.render() and\ - "-key mousey '300'" in cmd.render() + assert "-key doggy '100'" in cmd.render() and "-key mousey '300'" in cmd.render() # clear all cmd.key_vals.clear() - assert '-key' not in cmd.render() + assert "-key" not in cmd.render() def test_list_field(): @@ -69,7 +69,7 @@ def test_list_field(): # clear all del cmd.info_files[:] - assert '-inf' not in cmd.render() + assert "-inf" not in cmd.render() # three entries - two via 'info_files' and one via 'info_file' cmd.info_files = ["100", "200"] @@ -79,7 +79,7 @@ def test_list_field(): # clear all cmd.info_file = "" cmd.info_files[:] = [] - assert '-inf' not in cmd.render() + assert "-inf" not in cmd.render() def test_prefix(): @@ -87,7 +87,7 @@ def test_prefix(): pre = "doggy bath" cmd.prefix = pre # single quotes are added - assert cmd.render() == "'{}'".format(pre) + ' ' + assert cmd.render() == "'{}'".format(pre) + " " def test_addr_field(): @@ -95,12 +95,12 @@ def test_addr_field(): cmd.proxy_host = None assert not cmd.render() - cmd.proxy_host = '127.0.0.1' + cmd.proxy_host = "127.0.0.1" cmd.proxy_port = 5060 assert cmd.render() == "-rsa '127.0.0.1':'5060' " - cmd.proxy_host = '::1' + cmd.proxy_host = "::1" assert cmd.render() == "-rsa '[::1]':'5060' " - cmd.proxy_host = 'example.com' + cmd.proxy_host = "example.com" assert cmd.render() == "-rsa 'example.com':'5060' " diff --git a/tests/test_launcher.py b/tests/test_launcher.py index 5c17070..12b13d1 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -1,8 +1,6 @@ -''' +""" Basic agent/scenario launching -''' -import time - +""" import trio import pytest @@ -20,7 +18,7 @@ def run_blocking(runner, agents): def test_agent_fails(): uas = server(call_count=1) # apply bogus ip which can't be bound - uas.local_host, uas.local_port = '99.99.99.99', 5060 + uas.local_host, uas.local_port = "99.99.99.99", 5060 # client calls server at bogus addr uac = client(destaddr=(uas.local_host, uas.local_port)) uac.recv_timeout = 1 # avoids SIPp issue #176 diff --git a/tests/test_loader.py b/tests/test_loader.py index d7da25b..7711353 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -1,6 +1,6 @@ -''' +""" Scen dir loading -''' +""" import os from pysipp.load import iter_scen_dirs @@ -12,9 +12,9 @@ def test_scendir_loading(scendir): def test_iter_dirs(scendir): paths = { - 'default': [True, False], - 'default_with_confpy': [True, True], - 'just_confpy': [False, True], + "default": [True, False], + "default_with_confpy": [True, True], + "just_confpy": [False, True], } for path, xmls, confpy in iter_scen_dirs(scendir): expect = paths.get(os.path.basename(path), None) diff --git a/tests/test_stack.py b/tests/test_stack.py index 2b8b8f6..842cbf8 100644 --- a/tests/test_stack.py +++ b/tests/test_stack.py @@ -1,6 +1,6 @@ -''' +""" End to end tests with plugin support -''' +""" import pytest import pysipp import functools @@ -13,8 +13,7 @@ def scenwalk(scendir): def test_collect(scenwalk): - """Verify the scendir filtering hook - """ + """Verify the scendir filtering hook""" assert len(list(scenwalk())) == 2 # test filtering hook @@ -29,6 +28,7 @@ def pysipp_load_scendir(self, path, xmls, confpy): @pysipp.plugin.hookimpl def confpy_included(self, path, xmls, confpy): return bool(confpy) + blockall.pysipp_load_scendir = confpy_included pysipp.plugin.mng.register(blockall()) @@ -40,40 +40,38 @@ def test_confpy_hooks(scendir): Assertions here are based on predefined hooks """ - path, scen = list(pysipp.walk(scendir + '/default_with_confpy'))[0] + path, scen = list(pysipp.walk(scendir + "/default_with_confpy"))[0] assert scen.mod # ordering hook should reversed agents agents = list(scen.agents.values()) assert agents[0].is_client() assert agents[1].is_server() # check that `scen.remote_host = 'doggy'` was applied - assert scen.defaults.uri_username == 'doggy' + assert scen.defaults.uri_username == "doggy" for agent in scen.prepare(): - assert agent.uri_username == 'doggy' + assert agent.uri_username == "doggy" def test_proxyaddr_with_scendir(scendir): """When building a scenario from a xml file directory the `proxyaddr` kwarg should be assigned. """ - remoteaddr = ('9.9.9.9', 80) + remoteaddr = ("9.9.9.9", 80) scen = pysipp.scenario( - dirpath=scendir + '/default_with_confpy', - proxyaddr=remoteaddr + dirpath=scendir + "/default_with_confpy", proxyaddr=remoteaddr ) assert scen.clientdefaults.proxyaddr == remoteaddr for name, cmd in scen.cmditems(): - if name == 'uac': + if name == "uac": assert "-rsa '{}':'{}'".format(*remoteaddr) in cmd assert "'{}':'{}'".format(*scen.clientdefaults.destaddr) in cmd - elif name == 'uas': + elif name == "uas": assert "-rsa '{}':'{}'".format(*remoteaddr) not in cmd def test_sync_run(scenwalk): - """Ensure all scenarios in the test run to completion in synchronous mode - """ + """Ensure all scenarios in the test run to completion in synchronous mode""" for path, scen in scenwalk(): runner = scen(timeout=6) for cmd, proc in runner._procs.items(): @@ -81,8 +79,7 @@ def test_sync_run(scenwalk): def test_async_run(scenwalk): - """Ensure multiple scenarios run to completion in asynchronous mode. - """ + """Ensure multiple scenarios run to completion in asynchronous mode.""" finalizers = [] for path, scen in scenwalk(): finalizers.append((scen, scen(block=False))) @@ -94,8 +91,7 @@ def test_async_run(scenwalk): def test_basic(basic_scen): - """Test the most basic uac <-> uas call flow - """ + """Test the most basic uac <-> uas call flow""" assert len(basic_scen.agents) == 2 # ensure sync run works runner = basic_scen() @@ -107,7 +103,7 @@ def test_unreachable_uas(basic_scen): causing the uac to timeout on request responses. Ensure that an error is raised and that the appropriate log files are generated per agent. """ - uas = basic_scen.agents['uas'] + uas = basic_scen.agents["uas"] uas.proxyaddr = uas.local_host, 9999 with pytest.raises(RuntimeError): basic_scen() @@ -116,7 +112,7 @@ def test_unreachable_uas(basic_scen): for ua in basic_scen.prepare(): for name, path in ua.iter_logfile_items(): # randomly the -logfile stopped being generated? - if 'log' not in name: + if "log" not in name: assert os.path.isfile(path) os.remove(path) @@ -126,19 +122,21 @@ def test_hook_overrides(basic_scen): not override individual agent argument attrs that were set explicitly elsewhere (eg. in a hook). """ + class Router(object): @pysipp.plugin.hookimpl def pysipp_conf_scen(self, agents, scen): # no explicit port is set on agents by default - agents['uas'].local_port = 5090 - agents['uac'].remote_port = agents['uas'].local_port + agents["uas"].local_port = 5090 + agents["uac"].remote_port = agents["uas"].local_port with pysipp.plugin.register([Router()]): pysipp.plugin.mng.hook.pysipp_conf_scen( - agents=basic_scen.agents, scen=basic_scen) + agents=basic_scen.agents, scen=basic_scen + ) # apply a composite socket addr attr - basic_scen.clientdefaults['destaddr'] = '10.10.99.99', 'doggy' + basic_scen.clientdefaults["destaddr"] = "10.10.99.99", "doggy" # destaddr set in clientdefaults should not override agent values agents = basic_scen.prepare() @@ -148,20 +146,20 @@ def pysipp_conf_scen(self, agents, scen): @pytest.mark.parametrize( "dictname", - ['defaults', 'clientdefaults', 'serverdefaults'], + ["defaults", "clientdefaults", "serverdefaults"], ids=str, ) @pytest.mark.parametrize( "data", [ - {'local_host': '127.0.0.1'}, - {'local_port': 5080}, - {'local_port': 5080, 'local_host': '127.0.0.1'}, - {'srcaddr': ('127.0.0.1', 5080)}, - {'media_addr': '127.0.0.1'}, - {'media_port': 5080}, - {'media_port': 5080, 'media_addr': '127.0.0.1'}, - {'mediaaddr': ('127.0.0.1', 5080)}, + {"local_host": "127.0.0.1"}, + {"local_port": 5080}, + {"local_port": 5080, "local_host": "127.0.0.1"}, + {"srcaddr": ("127.0.0.1", 5080)}, + {"media_addr": "127.0.0.1"}, + {"media_port": 5080}, + {"media_port": 5080, "media_addr": "127.0.0.1"}, + {"mediaaddr": ("127.0.0.1", 5080)}, ], ids=lambda d: str(d), ) @@ -172,9 +170,9 @@ def test_autonet_overrides(dictname, data): scen = pysipp.scenario(**{dictname: data}) scen = scen.from_agents() # netplug.py hooks shouldn't override the uac srcaddr - if 'client' in dictname: + if "client" in dictname: agents = scen.clients - elif 'server' in dictname: + elif "server" in dictname: agents = scen.servers else: agents = scen.agents