[docs]defstart_sewerrat(db:Optional[str]=None,port:Optional[int]=None,wait:float=1,version:str="1.1.0",overwrite:bool=False)->Tuple[bool,int]:""" Start a test SewerRat service. Args: db: Path to a SQLite database. If None, one is automatically created. port: An available port. If None, one is automatically chosen. wait: Number of seconds to wait for the service to initialize before use. version: Version of the service to run. overwrite: Whether to overwrite the existing Gobbler binary. Returns: A tuple indicating whether a new test service was created (or an existing instance was re-used) and its URL. If a service is already running, this function is a no-op and the configuration details of the existing service will be returned. """globaltest_api_portiftest_api_processisnotNone:returnFalse,"http://0.0.0.0:"+str(test_api_port)exe=_acquire_sewerrat_binary(version,overwrite)_initialize_sewerrat_process(exe,db,port)time.sleep(1)# give it some time to spin up.returnTrue,"http://0.0.0.0:"+str(test_api_port)
def_acquire_sewerrat_binary(version:str,overwrite:bool):importplatformsysname=platform.system()ifsysname=="Darwin":OS="darwin"elifsysname=="Linux":OS="linux"else:raiseValueError("unsupported operating system '"+sysname+"'")sysmachine=platform.machine()ifsysmachine=="arm64":arch="arm64"elifsysmachine=="x86_64":arch="amd64"else:raiseValueError("unsupported architecture '"+sysmachine+"'")importappdirscache=appdirs.user_data_dir("SewerRat","aaron")desired="SewerRat-"+OS+"-"+archexe=os.path.join(cache,desired+"-"+version)ifnotos.path.exists(exe)oroverwrite:url="https://github.com/ArtifactDB/SewerRat/releases/download/"+version+"/"+desiredimportshutilos.makedirs(cache,exist_ok=True)tmp=exe+".tmp"ut.download_file(url,tmp)os.chmod(tmp,0o755)# Using a write-and-rename paradigm to provide some atomicity. Note# that renaming doesn't work across different filesystems so in that# case we just fall back to copying.try:shutil.move(tmp,exe)except:shutil.copy(tmp,exe)returnexedef_initialize_sewerrat_process(exe:str,db:Optional[str],port:Optional[int]):ifportisNone:importsocketwithsocket.socket(socket.AF_INET)ass:s.bind(('0.0.0.0',0))port=s.getsockname()[1]ifdbisNone:importtempfilehost=tempfile.mkdtemp()db=os.path.join(host,"index.sqlite3")globaltest_api_portglobaltest_api_processtest_api_port=portimportsubprocesstest_api_process=subprocess.Popen([exe,"-db",db,"-port",str(port)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)importatexitatexit.register(stop_sewerrat)return
[docs]defstop_sewerrat():""" Stop the SewerRat test service started by :py:func:`~.start_sewerrat`. If no test service was running, this function is a no-op. """globaltest_api_processglobaltest_api_portiftest_api_processisnotNone:test_api_process.terminate()test_api_process=Nonetest_api_port=Nonereturn