@@ -46,15 +46,15 @@ class WordSplitter(object):
4646 SQUOTE = "'"
4747 DQUOTE = '"'
4848
49- def __init__ (self , s , args = None , envs = None ):
49+ def __init__ (self , s : str , args = None , envs = None ):
5050 """
5151 :param s: str, string to process
5252 :param args: dict, build arguments to use; if None, do not
5353 attempt substitution
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,125 @@ 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 : str ):
359+ result = cls ()
360+
361+ if not image_name or image_name .isspace ():
362+ return ImageName ()
363+
364+ if isinstance (image_name , cls ):
365+ logger .debug ("Attempting to parse ImageName %s as an ImageName" , image_name )
366+ return image_name
367+
368+ # registry.org/namespace/repo:tag
369+ s = image_name .split ('/' , 2 )
370+
371+ if len (s ) == 2 :
372+ if '.' in s [0 ] or ':' in s [0 ]:
373+ result .registry = s [0 ] if s [0 ] else None
374+ else :
375+ result .namespace = s [0 ]
376+ elif len (s ) == 3 :
377+ result .registry = s [0 ] if s [0 ] else None
378+ result .namespace = s [1 ]
379+ result .repo = s [- 1 ]
380+
381+ for sep in '@:' :
382+ try :
383+ result .repo , result .tag = result .repo .rsplit (sep , 1 )
384+ except ValueError :
385+ continue
386+ break
387+
388+ return result
389+
390+ def to_str (self , registry = True , tag = True , explicit_tag = False ,
391+ explicit_namespace = False ):
392+ if self .repo is None :
393+ raise RuntimeError ('No image repository specified' )
394+
395+ result = self .get_repo (explicit_namespace )
396+
397+ if tag and self .tag and ':' in self .tag :
398+ result = '{0}@{1}' .format (result , self .tag )
399+ elif tag and self .tag :
400+ result = '{0}:{1}' .format (result , self .tag )
401+ elif tag and explicit_tag :
402+ result = '{0}:{1}' .format (result , 'latest' )
403+
404+ if registry and self .registry :
405+ result = '{0}/{1}' .format (self .registry , result )
406+
407+ return result
408+
409+ def get_repo (self , explicit_namespace = False ):
410+ result = self .repo
411+ if self .namespace :
412+ result = '{0}/{1}' .format (self .namespace , result )
413+ elif explicit_namespace :
414+ result = '{0}/{1}' .format ('library' , result )
415+ return result
416+
417+ def enclose (self , organization ):
418+ if self .namespace == organization :
419+ return
420+
421+ repo_parts = [self .repo ]
422+ if self .namespace :
423+ repo_parts .insert (0 , self .namespace )
424+
425+ self .namespace = organization
426+ self .repo = '-' .join (repo_parts )
427+
428+ def __str__ (self ):
429+ return self .to_str (registry = True , tag = True )
430+
431+ def __repr__ (self ):
432+ return (
433+ "ImageName(registry={s.registry!r}, namespace={s.namespace!r},"
434+ " repo={s.repo!r}, tag={s.tag!r})"
435+ ).format (s = self )
436+
437+ def __eq__ (self , other ):
438+ if type (other ) == str :
439+ return self .__str__ () == other
440+ elif type (other ) == type (self ):
441+ return self .__dict__ == other .__dict__
442+ else :
443+ return NotImplemented
444+
445+ def __ne__ (self , other ):
446+ return not self == other
447+
448+ def __hash__ (self ):
449+ return hash (self .to_str ())
450+
451+ def copy (self ):
452+ return ImageName (
453+ registry = self .registry ,
454+ namespace = self .namespace ,
455+ repo = self .repo ,
456+ tag = self .tag )
0 commit comments