From 93b9b6fd9fe23388682df75cc3b0212d4f6bb69f Mon Sep 17 00:00:00 2001 From: Christopher Grebs Date: Sun, 5 Jan 2014 18:26:32 -0800 Subject: [PATCH] First version with python3 support. * Moved requirements*.txt files to proper spec definitions in setup.py * Added a new fig.compat module to store some compatibility code --- MANIFEST.in | 4 +++- fig/__init__.py | 1 + fig/cli/colors.py | 1 + fig/cli/command.py | 4 +++- fig/cli/docopt_command.py | 2 ++ fig/cli/errors.py | 1 + fig/cli/formatter.py | 2 ++ fig/cli/log_printer.py | 4 +++- fig/cli/multiplexer.py | 1 + fig/cli/utils.py | 4 +++- fig/compat.py | 23 +++++++++++++++++++++++ fig/container.py | 4 +++- fig/project.py | 7 +++++-- fig/service.py | 10 ++++++---- requirements-dev.txt | 2 -- requirements.txt | 4 ---- setup.py | 30 ++++++++++++++++++++---------- tests/container_test.py | 1 + tests/project_test.py | 1 + tests/service_test.py | 2 ++ tests/testcases.py | 2 ++ 21 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 fig/compat.py delete mode 100644 requirements-dev.txt delete mode 100644 requirements.txt diff --git a/MANIFEST.in b/MANIFEST.in index a929a01cef..95e97bf385 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ include LICENSE include *.md -include requirements.txt +recursive-include tests * +global-exclude *.pyc +global-exclode *.pyo diff --git a/fig/__init__.py b/fig/__init__.py index 7d6d5a305c..baa08f1ed9 100644 --- a/fig/__init__.py +++ b/fig/__init__.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals from .service import Service __version__ = '0.0.2' diff --git a/fig/cli/colors.py b/fig/cli/colors.py index 09ec84bdb7..af4a32ab45 100644 --- a/fig/cli/colors.py +++ b/fig/cli/colors.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals NAMES = [ 'grey', 'red', diff --git a/fig/cli/command.py b/fig/cli/command.py index 813ea4db71..fb0adaf050 100644 --- a/fig/cli/command.py +++ b/fig/cli/command.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import from docker import Client import errno import logging @@ -21,7 +23,7 @@ class Command(DocoptCommand): def project(self): try: config = yaml.load(open('fig.yml')) - except IOError, e: + except IOError as e: if e.errno == errno.ENOENT: log.error("Can't find %s. Are you in the right directory?", e.filename) else: diff --git a/fig/cli/docopt_command.py b/fig/cli/docopt_command.py index d2aeb035fc..cbb8e5303c 100644 --- a/fig/cli/docopt_command.py +++ b/fig/cli/docopt_command.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import import sys from inspect import getdoc diff --git a/fig/cli/errors.py b/fig/cli/errors.py index 038a7ea18b..bb1702fda6 100644 --- a/fig/cli/errors.py +++ b/fig/cli/errors.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from textwrap import dedent diff --git a/fig/cli/formatter.py b/fig/cli/formatter.py index 55a967f9f2..b57c1895f6 100644 --- a/fig/cli/formatter.py +++ b/fig/cli/formatter.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import import texttable import os diff --git a/fig/cli/log_printer.py b/fig/cli/log_printer.py index 480fc5ebaf..f31fb9b874 100644 --- a/fig/cli/log_printer.py +++ b/fig/cli/log_printer.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import import sys from itertools import cycle @@ -41,7 +43,7 @@ class LogPrinter(object): 'stream': True, } params.update(self.attach_params) - params = dict((name, 1 if value else 0) for (name, value) in params.items()) + params = dict((name, 1 if value else 0) for (name, value) in list(params.items())) return container.attach_socket(params=params, ws=True) def read_websocket(websocket): diff --git a/fig/cli/multiplexer.py b/fig/cli/multiplexer.py index cfcc3b6ce3..579f3bcac3 100644 --- a/fig/cli/multiplexer.py +++ b/fig/cli/multiplexer.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from threading import Thread try: diff --git a/fig/cli/utils.py b/fig/cli/utils.py index a1ff0c008c..8e8ab3767a 100644 --- a/fig/cli/utils.py +++ b/fig/cli/utils.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import import datetime import os import socket @@ -69,7 +71,7 @@ def prettydate(d): return '{0} hours ago'.format(s/3600) -def mkdir(path, permissions=0700): +def mkdir(path, permissions=0o700): if not os.path.exists(path): os.mkdir(path) diff --git a/fig/compat.py b/fig/compat.py new file mode 100644 index 0000000000..5b01f6b3b7 --- /dev/null +++ b/fig/compat.py @@ -0,0 +1,23 @@ + + +# Taken from python2.7/3.3 functools +def cmp_to_key(mycmp): + """Convert a cmp= function into a key= function""" + class K(object): + __slots__ = ['obj'] + def __init__(self, obj): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + __hash__ = None + return K diff --git a/fig/container.py b/fig/container.py index 454ec91f66..9556ec1f6f 100644 --- a/fig/container.py +++ b/fig/container.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import import logging log = logging.getLogger(__name__) @@ -53,7 +55,7 @@ class Container(object): if not self.dictionary['NetworkSettings']['Ports']: return '' ports = [] - for private, public in self.dictionary['NetworkSettings']['Ports'].items(): + for private, public in list(self.dictionary['NetworkSettings']['Ports'].items()): if public: ports.append('%s->%s' % (public[0]['HostPort'], private)) return ', '.join(ports) diff --git a/fig/project.py b/fig/project.py index 97ff319db7..3c536ab6df 100644 --- a/fig/project.py +++ b/fig/project.py @@ -1,5 +1,8 @@ +from __future__ import unicode_literals +from __future__ import absolute_import import logging from .service import Service +from .compat import cmp_to_key log = logging.getLogger(__name__) @@ -13,7 +16,7 @@ def sort_service_dicts(services): elif y_deps_x and not x_deps_y: return -1 return 0 - return sorted(services, cmp=cmp) + return sorted(services, key=cmp_to_key(cmp)) class Project(object): """ @@ -43,7 +46,7 @@ class Project(object): @classmethod def from_config(cls, name, config, client): dicts = [] - for service_name, service in config.items(): + for service_name, service in list(config.items()): service['name'] = service_name dicts.append(service) return cls.from_dicts(name, dicts, client) diff --git a/fig/service.py b/fig/service.py index 3fb0e4287e..4571f8197a 100644 --- a/fig/service.py +++ b/fig/service.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import from docker.client import APIError import logging import re @@ -64,7 +66,7 @@ class Service(object): container_options = self._get_container_options(override_options, one_off=one_off) try: return Container.create(self.client, **container_options) - except APIError, e: + except APIError as e: if e.response.status_code == 404 and e.explanation and 'No such image' in e.explanation: log.info('Pulling image %s...' % container_options['image']) self.client.pull(container_options['image']) @@ -82,7 +84,7 @@ class Service(object): if options.get('ports', None) is not None: for port in options['ports']: - port = unicode(port) + port = str(port) if ':' in port: internal_port, external_port = port.split(':', 1) port_bindings[int(internal_port)] = int(external_port) @@ -107,7 +109,7 @@ class Service(object): bits = [self.project, self.name] if one_off: bits.append('run') - return '_'.join(bits + [unicode(self.next_container_number(one_off=one_off))]) + return '_'.join(bits + [str(self.next_container_number(one_off=one_off))]) def next_container_number(self, one_off=False): numbers = [parse_name(c.name)[2] for c in self.containers(stopped=True, one_off=one_off)] @@ -132,7 +134,7 @@ class Service(object): container_options['name'] = self.next_container_name(one_off) if 'ports' in container_options: - container_options['ports'] = [unicode(p).split(':')[0] for p in container_options['ports']] + container_options['ports'] = [str(p).split(':')[0] for p in container_options['ports']] if 'volumes' in container_options: container_options['volumes'] = dict((v.split(':')[1], {}) for v in container_options['volumes']) diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 4ef6576c49..0000000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,2 +0,0 @@ -nose==1.3.0 -unittest2==0.5.1 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index dccc0aa118..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -docker-py==0.2.3 -docopt==0.6.1 -PyYAML==3.10 -texttable==0.8.1 diff --git a/setup.py b/setup.py index 2f7131f68c..60b01a43bb 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,17 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - -from setuptools import setup +from __future__ import unicode_literals +from __future__ import absolute_import +from setuptools import setup, find_packages import re import os import codecs -# Borrowed from -# https://github.com/jezdez/django_compressor/blob/develop/setup.py def read(*parts): - return codecs.open(os.path.join(os.path.dirname(__file__), *parts)).read() + path = os.path.join(os.path.dirname(__file__), *parts) + with codecs.open(path, encoding='utf-8') as fobj: + return fobj.read() def find_version(*file_paths): @@ -21,8 +22,6 @@ def find_version(*file_paths): return version_match.group(1) raise RuntimeError("Unable to find version string.") -with open('requirements.txt') as f: - install_requires = f.read().splitlines() setup( name='fig', @@ -31,10 +30,21 @@ setup( url='https://github.com/orchardup/fig', author='Orchard Laboratories Ltd.', author_email='hello@orchardup.com', - packages=['fig', 'fig.cli'], - package_data={}, + packages=find_packages(), include_package_data=True, - install_requires=install_requires, + test_suite='nose.collector', + install_requires=[ + 'docker-py==0.2.3', + 'docopt==0.6.1', + 'PyYAML==3.10', + 'texttable==0.8.1', + # unfortunately `docker` requires six ==1.3.0 + 'six==1.3.0', + ], + tests_require=[ + 'nose==1.3.0', + 'unittest2==0.5.1' + ], entry_points=""" [console_scripts] fig=fig.cli.main:main diff --git a/tests/container_test.py b/tests/container_test.py index 0d6c5f0f62..3666b3e440 100644 --- a/tests/container_test.py +++ b/tests/container_test.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals from .testcases import DockerClientTestCase from fig.container import Container diff --git a/tests/project_test.py b/tests/project_test.py index 82b9be9df7..09792fabdd 100644 --- a/tests/project_test.py +++ b/tests/project_test.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals from fig.project import Project from .testcases import DockerClientTestCase diff --git a/tests/service_test.py b/tests/service_test.py index f946ab38f8..bc5c6ffe9c 100644 --- a/tests/service_test.py +++ b/tests/service_test.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import from fig import Service from .testcases import DockerClientTestCase diff --git a/tests/testcases.py b/tests/testcases.py index 4ac61d9d7d..671e091b7c 100644 --- a/tests/testcases.py +++ b/tests/testcases.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +from __future__ import absolute_import from docker import Client from fig.service import Service from fig.cli.utils import docker_url