[docs]defstart_sewerrat(db:Optional[str]=None,port:Optional[int]=None,wait:float=1,version:str="1.3.1",whitelist:Optional[list]=None,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. whitelist: List of users who can create symbolic links that will be followed by the SewerRat service. If None, this defaults to the current user and the owner of the temporary directory. 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,whitelist)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],whitelist:Optional[list]):ifwhitelistisNone:importgetpasswhitelist=set([getpass.getuser()])importtempfileimportpathlibtmp=pathlib.Path(tempfile.gettempdir())whileTrue:whitelist.add(tmp.owner())parent=tmp.parentifparent==tmp:breaktmp=parentwhitelist=list(whitelist)ifdbisNone:importtempfilehost=tempfile.mkdtemp()db=os.path.join(host,"index.sqlite3")ifportisNone:importsocketwithsocket.socket(socket.AF_INET)ass:s.bind(('0.0.0.0',0))port=s.getsockname()[1]globaltest_api_portglobaltest_api_processtest_api_port=portimportsubprocesstest_api_process=subprocess.Popen([exe,"-db",db,"-port",str(port),"-whitelist",",".join(whitelist)],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