Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop support for Python 2.6 and 3.2 #119

Merged
merged 5 commits into from
Sep 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ services:

language: python
python:
- "2.6"
- "2.7"
- "3.2"
- "3.3"
- "3.4"
- "3.5"
- "3.5-dev" # 3.5 development branch
- "3.6"
- "nightly" # currently points to 3.6-dev

matrix:
Expand Down
28 changes: 14 additions & 14 deletions scuba/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,23 @@ def prepare(self):
def __str__(self):
s = StringIO()
writeln(s, 'ScubaDive')
writeln(s, ' verbose: {0}'.format(self.verbose))
writeln(s, ' as_root: {0}'.format(self.as_root))
writeln(s, ' workdir: {0}'.format(self.workdir))
writeln(s, ' verbose: {}'.format(self.verbose))
writeln(s, ' as_root: {}'.format(self.as_root))
writeln(s, ' workdir: {}'.format(self.workdir))

writeln(s, ' options:')
for a in self.options:
writeln(s, ' ' + a)

writeln(s, ' env_vars:')
for k,v in self.env_vars.items():
writeln(s, ' {0}={1}'.format(k, v))
writeln(s, ' {}={}'.format(k, v))

writeln(s, ' volumes:')
for hostpath, contpath, options in self.__get_vol_opts():
writeln(s, ' {0} => {1} {2}'.format(hostpath, contpath, options))
writeln(s, ' {} => {} {}'.format(hostpath, contpath, options))

writeln(s, ' user_command: {0}'.format(self.user_command))
writeln(s, ' user_command: {}'.format(self.user_command))
writeln(s, ' context:')
writeln(s, ' script: ' + str(self.context.script))
writeln(s, ' image: ' + str(self.context.image))
Expand Down Expand Up @@ -199,7 +199,7 @@ def __locate_scubainit(self):

self.scubainit_path = os.path.join(pkg_path, 'scubainit')
if not os.path.isfile(self.scubainit_path):
raise ScubaError('scubainit not found at "{0}"'.format(self.scubainit_path))
raise ScubaError('scubainit not found at "{}"'.format(self.scubainit_path))


def __load_config(self):
Expand Down Expand Up @@ -244,7 +244,7 @@ def __setup_native_run(self):


# Pass variables to scubainit
self.add_env('SCUBAINIT_UMASK', '{0:04o}'.format(get_umask()))
self.add_env('SCUBAINIT_UMASK', '{:04o}'.format(get_umask()))

if not self.as_root:
self.add_env('SCUBAINIT_UID', os.getuid())
Expand Down Expand Up @@ -288,13 +288,13 @@ def __setup_native_run(self):
default_cmd = get_image_command(context.image)
if not default_cmd:
raise ScubaError('No command given and no image-specified command')
verbose_msg('{0} Cmd: "{1}"'.format(context.image, default_cmd))
verbose_msg('{} Cmd: "{}"'.format(context.image, default_cmd))
context.script = [shell_quote_cmd(default_cmd)]

# Make scubainit the entrypoint, and manually insert an existing
# entrypoint before each user command
entrypoint = get_image_entrypoint(context.image) or []
self.add_option('--entrypoint={0}'.format(scubainit_cpath))
self.add_option('--entrypoint={}'.format(scubainit_cpath))

# The user command is executed via a generated shell script
with self.open_scubadir_file('command.sh', 'wt') as f:
Expand Down Expand Up @@ -346,9 +346,9 @@ def __generate_hook_script(self, name):
return

# Generate the hook script, mount it into the container, and tell scubainit
with self.open_scubadir_file('hooks/{0}.sh'.format(name), 'wt') as f:
with self.open_scubadir_file('hooks/{}.sh'.format(name), 'wt') as f:

self.add_env('SCUBAINIT_HOOK_{0}'.format(name.upper()), f.container_path)
self.add_env('SCUBAINIT_HOOK_{}'.format(name.upper()), f.container_path)

writeln(f, '#!/bin/sh')
writeln(f, '# Auto-generated from .scuba.yml')
Expand All @@ -370,7 +370,7 @@ def get_docker_cmdline(self):
]

for name,val in self.env_vars.items():
args.append('--env={0}={1}'.format(name, val))
args.append('--env={}={}'.format(name, val))

for hostpath, contpath, options in self.__get_vol_opts():
args.append(make_vol_opt(hostpath, contpath, options))
Expand Down Expand Up @@ -403,7 +403,7 @@ def run_scuba(scuba_args):
print('ALIAS\tIMAGE')
for name in sorted(dive.config.aliases):
alias = dive.config.aliases[name]
print('{0}\t{1}'.format(alias.name, alias.image or dive.config.image))
print('{}\t{}'.format(alias.name, alias.image or dive.config.image))
return

try:
Expand Down
22 changes: 0 additions & 22 deletions scuba/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
__all__ = [
'File',
'StringIO',
'check_output',
]


Expand All @@ -29,24 +28,3 @@ class File(file):
# 'file' type removed, but open() returns _io.TextIOWrapper
# which has a __dict__
File = open


# check_output
# A replacement for subprocess.check_output() which doesn't exist in Python 2.6
# https://github.com/python/cpython/blob/v2.7.13/Lib/subprocess.py#L190
try:
check_output = subprocess.check_output
except AttributeError:
def check_output(*popenargs, **kwargs):
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
# Python 2.6 CalledProcessError doesn't accept output arg
raise subprocess.CalledProcessError(retcode, cmd)
return output
26 changes: 13 additions & 13 deletions scuba/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def from_yaml(self, node):

# Split on unquoted spaces
try:
parts = shlex_split(content)
parts = shlex.split(content)
except UnicodeEncodeError:
raise yaml.YAMLError('Non-ASCII arguments to !from_yaml are unsupported')

Expand All @@ -65,7 +65,7 @@ def from_yaml(self, node):
for k in key.split('.'):
cur = cur[k]
except KeyError:
raise yaml.YAMLError('Key "{0}" not found in {1}'.format(key, filename))
raise yaml.YAMLError('Key "{}" not found in {}'.format(key, filename))
return cur

Loader.add_constructor('!from_yaml', Loader.from_yaml)
Expand All @@ -88,14 +88,14 @@ def find_config():
return path, rel

if not cross_fs and os.path.ismount(path):
msg = '{0} not found here or any parent up to mount point {1}'.format(SCUBA_YML, path) \
msg = '{} not found here or any parent up to mount point {}'.format(SCUBA_YML, path) \
+ '\nStopping at filesystem boundary (SCUBA_DISCOVERY_ACROSS_FILESYSTEM not set).'
raise ConfigNotFoundError(msg)

# Traverse up directory hierarchy
path, rest = os.path.split(path)
if not rest:
raise ConfigNotFoundError('{0} not found here or any parent directories'.format(SCUBA_YML))
raise ConfigNotFoundError('{} not found here or any parent directories'.format(SCUBA_YML))

# Accumulate the relative path back to where we started
rel = os.path.join(rest, rel)
Expand All @@ -116,17 +116,17 @@ def _process_script_node(node, name):
# There must be a "script" key, which must be a list of strings
script = node.get('script')
if not script:
raise ConfigError("{0}: must have a 'script' subkey".format(name))
raise ConfigError("{}: must have a 'script' subkey".format(name))

if isinstance(script, list):
return script

if isinstance(script, basestring):
return [script]

raise ConfigError("{0}.script: must be a string or list".format(name))
raise ConfigError("{}.script: must be a string or list".format(name))

raise ConfigError("{0}: must be string or dict".format(name))
raise ConfigError("{}: must be string or dict".format(name))


def _process_environment(node, name):
Expand All @@ -146,7 +146,7 @@ def _process_environment(node, name):
k, v = parse_env_var(e)
result[k] = v
else:
raise ConfigError("'{0}' must be list or mapping, not {1}".format(
raise ConfigError("'{}' must be list or mapping, not {}".format(
name, type(node).__name__))

return result
Expand All @@ -170,7 +170,7 @@ def from_dict(cls, name, node):
image = node.get('image')
environment = _process_environment(
node.get('environment'),
'{0}.{1}'.format(name, 'environment'))
'{}.{}'.format(name, 'environment'))

return cls(name, script, image, environment)

Expand All @@ -185,13 +185,13 @@ def __init__(self, **data):
# Check for missing required nodes
missing = [n for n in required_nodes if not n in data]
if missing:
raise ConfigError('{0}: Required node{1} missing: {2}'.format(SCUBA_YML,
raise ConfigError('{}: Required node{} missing: {}'.format(SCUBA_YML,
's' if len(missing) > 1 else '', ', '.join(missing)))

# Check for unrecognized nodes
extra = [n for n in data if not n in required_nodes + optional_nodes]
if extra:
raise ConfigError('{0}: Unrecognized node{1}: {2}'.format(SCUBA_YML,
raise ConfigError('{}: Unrecognized node{}: {}'.format(SCUBA_YML,
's' if len(extra) > 1 else '', ', '.join(extra)))

self._image = data['image']
Expand Down Expand Up @@ -293,8 +293,8 @@ def load_config(path):
with open(path) as f:
data = yaml.load(f, Loader)
except IOError as e:
raise ConfigError('Error opening {0}: {1}'.format(SCUBA_YML, e))
raise ConfigError('Error opening {}: {}'.format(SCUBA_YML, e))
except yaml.YAMLError as e:
raise ConfigError('Error loading {0}: {1}'.format(SCUBA_YML, e))
raise ConfigError('Error loading {}: {}'.format(SCUBA_YML, e))

return ScubaConfig(**(data or {}))
12 changes: 6 additions & 6 deletions scuba/dockerutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, image):
self.image = image

def __str__(self):
return 'No such image: {0}'.format(self.image)
return 'No such image: {}'.format(self.image)


def __wrap_docker_exec(func):
Expand Down Expand Up @@ -46,7 +46,7 @@ def docker_inspect(image):
if not p.returncode == 0:
if 'no such image' in stderr.lower():
raise NoSuchImageError(image)
raise DockerError('Failed to inspect image: {0}'.format(stderr.strip()))
raise DockerError('Failed to inspect image: {}'.format(stderr.strip()))

return json.loads(stdout)[0]

Expand All @@ -57,7 +57,7 @@ def docker_pull(image):
# If this fails, the default docker stdout/stderr looks good to the user.
ret = call(args)
if ret != 0:
raise DockerError('Failed to pull image "{0}"'.format(image))
raise DockerError('Failed to pull image "{}"'.format(image))

def docker_inspect_or_pull(image):
'''Inspects a docker image, pulling it if it doesn't exist'''
Expand All @@ -74,20 +74,20 @@ def get_image_command(image):
try:
return info['Config']['Cmd']
except KeyError as ke:
raise DockerError('Failed to inspect image: JSON result missing key {0}'.format(ke))
raise DockerError('Failed to inspect image: JSON result missing key {}'.format(ke))

def get_image_entrypoint(image):
'''Gets the image entrypoint'''
info = docker_inspect_or_pull(image)
try:
return info['Config']['Entrypoint']
except KeyError as ke:
raise DockerError('Failed to inspect image: JSON result missing key {0}'.format(ke))
raise DockerError('Failed to inspect image: JSON result missing key {}'.format(ke))


def make_vol_opt(hostdir, contdir, options=None):
'''Generate a docker volume option'''
vol = '--volume={0}:{1}'.format(hostdir, contdir)
vol = '--volume={}:{}'.format(hostdir, contdir)
if options != None:
if isinstance(options, str):
options = (options,)
Expand Down
8 changes: 0 additions & 8 deletions scuba/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import errno
import os
import shlex
try:
from shlex import quote as shell_quote
except ImportError:
Expand All @@ -10,13 +9,6 @@
def shell_quote_cmd(cmdlist):
return ' '.join(map(shell_quote, cmdlist))

def shlex_split(s):
# shlex.split doesn't properly handle unicode input in Python 2.6.
# First try to encode it as an ASCII string. which
# may raise a UnicodeEncodeError.
s = str(s)
return shlex.split(s)


def format_cmdline(args, maxwidth=80):
'''Format args into a shell-quoted command line.
Expand Down
7 changes: 3 additions & 4 deletions scuba/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
git_archive_rev = "$Format:%h$"

def git_describe():
from subprocess import check_call
from .compat import check_output # Py2.6
from subprocess import check_call, check_output

# Get the version from the local Git repository
check_call(['git', 'update-index', '-q', '--refresh'], cwd=PROJPATH)
Expand Down Expand Up @@ -49,13 +48,13 @@ def get_version():
if commits == 0 and not rev.endswith('dirty'):
return BASE_VERSION

return '{0}+{1}-{2}'.format(BASE_VERSION, commits, rev)
return '{}+{}-{}'.format(BASE_VERSION, commits, rev)


# Git archive
# If this was produced via `git archive`, we'll use the version it provides
if not git_archive_rev.startswith('$'):
return '{0}+g{1}'.format(BASE_VERSION, git_archive_rev)
return '{}+g{}'.format(BASE_VERSION, git_archive_rev)


# Package resource
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def get_version():
build_num = os.getenv('TRAVIS_BUILD_NUMBER')
build_tag = os.getenv('TRAVIS_TAG')
if (not build_tag) and (build_num != None):
return '{0}.{1}'.format(scuba.version.BASE_VERSION, build_num)
return '{}.{}'.format(scuba.version.BASE_VERSION, build_num)

return scuba.version.__version__

Expand Down
15 changes: 0 additions & 15 deletions tests/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,3 @@ def test_File(self):
from scuba.compat import File
f = File('/etc/passwd', 'rt')
f.we_can_add_new_attrs = True

def test_check_output_works(self):
from scuba.compat import check_output
msg = 'hello world'
out = check_output(['/bin/echo', '-n', msg])
out = out.decode()
self.assertEqual(out, msg)

def test_check_output_raises(self):
from scuba.compat import check_output
from subprocess import CalledProcessError

def do_it():
check_output(['/bin/false'])
self.assertRaises(CalledProcessError, do_it)
Loading