[docs]defupload_directory(project:str,asset:str,version:str,directory:str,staging:str,url:str,probation:bool=False,consume:Optional[bool]=None,ignore_dot:bool=True,spoof:Optional[str]=None,concurrent:int=1,):""" Upload a directory as a new versioned asset of a project in the registry. Args: project: The name of an existing project. asset: The name of a new or existing asset in ``project``. version: The name of a new version of ``asset``. directory: Path to a directory to be uploaded. For best performace, this should be a subdirectory of ``staging``, e.g., as created by :py:func:`~.allocate_upload_directory`. staging: Path to the staging directory. url: URL for the Gobbler REST API. probation: Whether to upload a probational version. consume: Whether the contents of ``directory`` can be consumed by the upload process. If true, the Gobbler will attempt to move files from ``directory`` into the registry. Otherwise, the contents of ``directory`` will not be modified by the upload. Defaults to true if the contents of ``directory`` need to be copied to ``staging``. ignore_dot: Whether to skip dotfiles in ``directory`` during upload. spoof: String containing the name of a user on whose behalf this request is being made. This should only be used if the Gobbler service allows spoofing by the current user. If ``None``, no spoofing is performed. concurrent: Number of concurrent copies. """# Normalizing them so that they're comparable, in order to figure out whether 'directory' lies inside 'staging'.directory=os.path.normpath(directory)staging=os.path.normpath(staging)in_staging=Falsetmpd=directorywhilelen(tmpd)>len(staging):tmpd=os.path.dirname(tmpd)iftmpd==staging:in_staging=Truebreakpurge_newdir=Falsetry:ifnotin_staging:newdir=allocate_upload_directory(staging)# If we're copying everything to our own staging directory, we can# delete it afterwards without affecting the user. We do this# clean-up to free up storage in the staging space.purge_newdir=Trueto_copy=[]forroot,dirs,filesinos.walk(directory):fordindirs:src=os.path.join(root,d)rel=os.path.relpath(src,directory)os.mkdir(os.path.join(newdir,rel))forfinfiles:src=os.path.join(root,f)rel=os.path.relpath(src,directory)dest=os.path.join(newdir,rel)slink=""ifos.path.islink(src):slink=os.readlink(src)ifslink=="":to_copy.append((src,dest))else:os.symlink(slink,dest)directory=newdirifconcurrent==1:foryinto_copy:_transfer_file(y)else:importmultiprocessingwithmultiprocessing.Pool(concurrent)asp:p.map(_transfer_file,to_copy)ifconsumeisNone:# If we copied everything over to our own staging directory, we're entitled to consume its contents.consume=notin_stagingreq={"source":os.path.basename(directory),"project":project,"asset":asset,"version":version,"on_probation":probation,"consume":consume,"ignore_dot":ignore_dot}ut.dump_request(staging,url,"upload",req,spoof=spoof)returnfinally:ifpurge_newdir:importshutilshutil.rmtree(newdir)
def_transfer_file(info:Tuple):src,dest=infoimportshutilshutil.copy(src,dest)sstat=os.stat(src)dstat=os.stat(dest)ifsstat.st_size!=dstat.st_size:raiseValueError("mismatch in sizes after copy ("+str(sstat.st_size)+" vs "+str(dstat.st_size)+")")smd5=compute_md5sum(src)dmd5=compute_md5sum(src)ifsmd5!=dmd5:raiseValueError("mismatch in MD5 checksums after copy ("+str(smd5)+" vs "+str(dmd5)+")")