Skip to content

Commit f6502b9

Browse files
committed
Move ImageName from osbs-client to util.
Refactor to keep string behavior identical for parser.
1 parent 1d4f9fa commit f6502b9

File tree

3 files changed

+428
-226
lines changed

3 files changed

+428
-226
lines changed

dockerfile_parse/parser.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from .constants import DOCKERFILE_FILENAME, COMMENT_INSTRUCTION
2121
from .util import (b2u, extract_key_values, get_key_val_dictionary,
22-
u2b, Context, WordSplitter)
22+
u2b, Context, WordSplitter, ImageName)
2323

2424

2525
logger = logging.getLogger(__name__)
@@ -880,7 +880,9 @@ def image_from(from_value):
880880
)?
881881
""")
882882
match = re.match(regex, from_value)
883-
return match.group('image', 'name') if match else (None, None)
883+
image = ImageName.parse(match.group('image')) if match else None
884+
name = match.group('name') if match else None
885+
return image, name
884886

885887

886888
def _endline(line):

dockerfile_parse/util.py

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)