@@ -54,7 +54,7 @@ def __init__(self, s, args=None, envs=None):
5454 :param envs: dict, environment variables to use; if None, do not
5555 attempt substitution
5656 """
57- self .stream = StringIO (s )
57+ self .stream = StringIO (str ( s ) )
5858 self .args = args
5959 self .envs = envs
6060
@@ -332,3 +332,121 @@ def get_values(self, context_type):
332332 if context_type .upper () == "LABEL" :
333333 return self .labels
334334 raise ValueError ("Unexpected context type: " + context_type )
335+
336+
337+ class ImageName (object ):
338+ """Represent an image.
339+ Naming Conventions
340+ ==================
341+ registry.somewhere/namespace/image_name:tag
342+ |-----------------| registry, reg_uri
343+ |---------| namespace
344+ |--------------------------------------| repository
345+ |--------------------| image name
346+ |--| tag
347+ |------------------------| image
348+ |------------------------------------------| image
349+ """
350+
351+ def __init__ (self , registry = None , namespace = None , repo = None , tag = None ):
352+ self .registry = registry
353+ self .namespace = namespace
354+ self .repo = repo
355+ self .tag = tag
356+
357+ @classmethod
358+ def parse (cls , image_name ):
359+ result = cls ()
360+
361+ if not image_name or image_name .isspace ():
362+ return ImageName ()
363+
364+ if isinstance (image_name , cls ):
365+ return image_name
366+
367+ # registry.org/namespace/repo:tag
368+ s = image_name .split ('/' , 2 )
369+
370+ if len (s ) == 2 :
371+ if '.' in s [0 ] or ':' in s [0 ]:
372+ result .registry = s [0 ] if s [0 ] else None
373+ else :
374+ result .namespace = s [0 ]
375+ elif len (s ) == 3 :
376+ result .registry = s [0 ] if s [0 ] else None
377+ result .namespace = s [1 ]
378+ result .repo = s [- 1 ]
379+
380+ for sep in '@:' :
381+ try :
382+ result .repo , result .tag = result .repo .rsplit (sep , 1 )
383+ except ValueError :
384+ continue
385+ break
386+
387+ return result
388+
389+ def to_str (self , registry = True , tag = True , explicit_tag = False ,
390+ explicit_namespace = False ):
391+ if self .repo is None :
392+ raise RuntimeError ('No image repository specified' )
393+
394+ result = self .get_repo (explicit_namespace )
395+
396+ if tag and self .tag and ':' in self .tag :
397+ result = '{0}@{1}' .format (result , self .tag )
398+ elif tag and self .tag :
399+ result = '{0}:{1}' .format (result , self .tag )
400+ elif tag and explicit_tag :
401+ result = '{0}:{1}' .format (result , 'latest' )
402+
403+ if registry and self .registry :
404+ result = '{0}/{1}' .format (self .registry , result )
405+
406+ return result
407+
408+ def get_repo (self , explicit_namespace = False ):
409+ result = self .repo
410+ if self .namespace :
411+ result = '{0}/{1}' .format (self .namespace , result )
412+ elif explicit_namespace :
413+ result = '{0}/{1}' .format ('library' , result )
414+ return result
415+
416+ def enclose (self , organization ):
417+ if self .namespace == organization :
418+ return
419+
420+ repo_parts = [self .repo ]
421+ if self .namespace :
422+ repo_parts .insert (0 , self .namespace )
423+
424+ self .namespace = organization
425+ self .repo = '-' .join (repo_parts )
426+
427+ def __str__ (self ):
428+ return self .to_str (registry = True , tag = True )
429+
430+ def __repr__ (self ):
431+ return (
432+ "ImageName(registry={s.registry!r}, namespace={s.namespace!r},"
433+ " repo={s.repo!r}, tag={s.tag!r})"
434+ ).format (s = self )
435+
436+ def __eq__ (self , other ):
437+ if type (other ) == str :
438+ return self .__str__ () == other
439+ elif type (other ) == type (self ):
440+ return self .__dict__ == other .__dict__
441+ else :
442+ return NotImplemented
443+
444+ def __hash__ (self ):
445+ return hash (self .to_str ())
446+
447+ def copy (self ):
448+ return ImageName (
449+ registry = self .registry ,
450+ namespace = self .namespace ,
451+ repo = self .repo ,
452+ tag = self .tag )
0 commit comments