add docstrings and format regex
MODIFY
repo2docker/utils.py
repo2docker/utils.py
from contextlib import contextmanager
from functools import partial
+import re
import shutil
import subprocess
-import re
import sys
from traitlets import Integer
+
def execute_cmd(cmd, capture=False, **kwargs):
"""
Call given command, yielding output line by line if capture=True
@@ -18,8 +19,7 @@ def execute_cmd(cmd, capture=False, **kwargs):
proc = subprocess.Popen(cmd, **kwargs)
if not capture:
- # not capturing output, let the subprocesses talk directly
- # to the terminal
+ # not capturing output, let subprocesses talk directly to terminal
ret = proc.wait()
if ret != 0:
raise subprocess.CalledProcessError(ret, cmd)
@@ -32,6 +32,7 @@ def execute_cmd(cmd, capture=False, **kwargs):
buf = []
def flush():
+ """Flush next line of the buffer"""
line = b''.join(buf).decode('utf8', 'replace')
buf[:] = []
return line
@@ -53,6 +54,7 @@ def execute_cmd(cmd, capture=False, **kwargs):
@contextmanager
def maybe_cleanup(path, cleanup=False):
+ """Execute cleanup of the passed path when cleanup flag is True."""
yield
if cleanup:
shutil.rmtree(path, ignore_errors=True)
@@ -60,35 +62,43 @@ def maybe_cleanup(path, cleanup=False):
def validate_and_generate_port_mapping(port_mapping):
"""
- Validate the port mapping list provided as argument and split into as dictionary of key being continer port and the
- values being None, or 'host_port' or ['interface_ip','host_port']
+ Validate the port mapping list and return a list of validated tuples.
+
+ Each entry in the passed port mapping list will be converted to a
+ tuple with a containing a string with the format 'key:value' with the
+ `key` being the container's port and the
+ `value` being `None`, `host_port` or `['interface_ip','host_port']`
Args:
- port_mapping (list): List of strings of format 'host_port:container_port'
- with optional tcp udp values and host network interface
+ port_mapping (list): List of strings of format
+ `'host_port:container_port'` with optional tcp udp values and host
+ network interface
Returns:
- List of validated tuples of form ('host_port:container_port') with optional tcp udp values and host network interface
+ List of validated tuples of form ('host_port:container_port') with
+ optional tcp udp values and host network interface
Raises:
Exception on invalid port mapping
Note:
- One limitation cannot bind single container_port to multiple host_ports (docker-py supports this but repo2docker
- does not)
+ One limitation of repo2docker is it cannot bind a
+ single container_port to multiple host_ports
+ (docker-py supports this but repo2docker does not)
Examples:
- Valid port mappings are
- 127.0.0.1:90:900
- :999 - To match to any host port
- 999:999/tcp - bind 999 host port to 999 tcp container port
-
- Invalid port mapping
- 127.0.0.1::999 --- even though docker accepts it
- other invalid ip address combinations
+ Valid port mappings are:
+ - `127.0.0.1:90:900`
+ - `:999` (match to any host port)
+ - `999:999/tcp` (bind 999 host port to 999 tcp container port)
+
+ Invalid port mapping:
+ - `127.0.0.1::999` (even though docker accepts it)
+ - other invalid ip address combinations
"""
- reg_regex = re.compile('''^(
+ reg_regex = re.compile(r"""
+ ^(
( # or capturing group
(?: # start capturing ip address of network interface
(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3} # first three parts
@@ -101,7 +111,8 @@ def validate_and_generate_port_mapping(port_mapping):
:
(?:6553[0-5]|655[0-2][0-9]|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|(\d){0,4})
(?:/udp|/tcp)?
- )$''', re.VERBOSE)
+ )$
+ """, re.VERBOSE)
ports = {}
if not port_mapping:
return None
@@ -120,38 +131,42 @@ def validate_and_generate_port_mapping(port_mapping):
host_port = port_host[0] if len(port_host[0]) > 0 else None
container_port = port_host[1]
-
if host is None:
ports[str(container_port)] = host_port
else:
ports[str(container_port)] = (host, host_port)
return ports
+
def is_valid_docker_image_name(image_name):
"""
- Function that constructs a regex representing the docker image name and tests it against the given image_name
- Reference Regex definition in https://github.com/docker/distribution/blob/master/reference/regexp.go
+ Determine if image name is valid for docker using strict pattern.
+
+ Function that constructs a regex representing the docker image name and
+ tests it against the given image_name. Reference Regex definition in
+ https://github.com/docker/distribution/blob/master/reference/regexp.go
+ The definition uses a stricter pattern than the docker default.
Args:
image_name: string representing a docker image name
Returns:
- True if image_name is valid else False
+ True if image_name is valid, else False
Example:
-
'test.Com/name:latest' is a valid tag
'Test/name:latest' is not a valid tag
Note:
+ This function has a stricter pattern than
+ https://github.com/docker/distribution/blob/master/reference/regexp.go
- This function has a stricter pattern than https://github.com/docker/distribution/blob/master/reference/regexp.go
-
- This pattern will not allow cases like
- 'TEST.com/name:latest' though docker considers it a valid tag
+ This pattern will not allow cases like `TEST.com/name:latest` though
+ docker considers it a valid tag.
"""
- reference_regex = re.compile(r"""^ # Anchored at start and end of string
+ reference_regex = re.compile(r"""
+ ^ # Anchored at start and end of string
( # Start capturing name
@@ -161,7 +176,8 @@ def is_valid_docker_image_name(image_name):
(?: # start optional group
- (?:\.(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+ # multiple repetitions of pattern '.<domain-name-component>'
+ # multiple repetitions of pattern '.<domain-name-component>'
+ (?:\.(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+
)? # end optional grouping part of the '.' separated domain name
@@ -195,9 +211,10 @@ def is_valid_docker_image_name(image_name):
) # end capturing name
(?::([\w][\w.-]{0,127}))? # optional capture <tag-pattern>=':<tag>'
- (?:@[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,})? # optionally capture <digest-pattern>='@<digest>'
- $""",
- re.VERBOSE)
+ # optionally capture <digest-pattern>='@<digest>'
+ (?:@[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,})?
+ $
+ """, re.VERBOSE)
return reference_regex.match(image_name) is not None
@@ -227,11 +244,11 @@ class ByteSpecification(Integer):
def validate(self, obj, value):
"""
- Validate that the passed in value is a valid memory specification
+ Validate that the passed-in value is a valid memory specification
- It could either be a pure int, when it is taken as a byte value.
- If it has one of the suffixes, it is converted into the appropriate
- pure byte value.
+ If value is a pure int, it is taken as a byte value.
+ If value has one of the unit suffixes, it is converted into the
+ appropriate pure byte value.
"""
if isinstance(value, (int, float)):
return int(value)
@@ -239,9 +256,17 @@ class ByteSpecification(Integer):
try:
num = float(value[:-1])
except ValueError:
- raise TraitError('{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(val=value))
+ raise TraitError(
+ '{val} is not a valid memory specification. '
+ 'Must be an int or a string with suffix K, M, G, T'
+ .format(val=value)
+ )
suffix = value[-1]
if suffix not in self.UNIT_SUFFIXES:
- raise TraitError('{val} is not a valid memory specification. Must be an int or a string with suffix K, M, G, T'.format(val=value))
+ raise TraitError(
+ '{val} is not a valid memory specification. '
+ 'Must be an int or a string with suffix K, M, G, T'
+ .format(val=value)
+ )
else:
return int(float(num) * self.UNIT_SUFFIXES[suffix])

add docstrings and format regex