prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Issue with port option in 2.4.0 version
  Hi,
  I update to the 2.4 today and i got issue with docker-compose when i try to add the following line to my configuration file (docker-compose.yml) : 
  `ports:
              - "127.0.0.1:9292:9090"`
  
  I got the following error:
  
  `
  ERROR: for ContainerName  expected string or buffer
  Traceback (most recent call last):
    File "/usr/local/bin/docker-compose", line 11, in <module>
      sys.exit(main())
    File "/usr/local/lib/python2.7/dist-packages/compose/cli/main.py", line 68, in main
      command()
    File "/usr/local/lib/python2.7/dist-packages/compose/cli/main.py", line 118, in perform_command
      handler(command, command_options)
    File "/usr/local/lib/python2.7/dist-packages/compose/cli/main.py", line 926, in up
      scale_override=parse_scale_args(options['--scale']),
    File "/usr/local/lib/python2.7/dist-packages/compose/project.py", line 424, in up
      get_deps
    File "/usr/local/lib/python2.7/dist-packages/compose/parallel.py", line 69, in parallel_execute
      raise error_to_reraise
  TypeError: expected string or buffer
  `
  
  I have no issue when i downgrade again to the 2.3 version of the package
  
  To reproduce the issue, i use the following configuration ( it doesn't seem to depend on the image):
  ```
  version: '2'
  
  services :
      ContainerName:
          image: bae2d441e03a
          ports:
              - "127.0.0.1:9292:9090"
  ```
  
  I run on Ubuntu 14.04.5 LTS with the following package:
  ```
  docker==2.4.0
  docker-compose==1.14.0
  docker-pycreds==0.2.1
  dockerpty==0.4.1
  Python 2.7.6
  Client:
   Version:      17.05.0-ce
   API version:  1.29
   Go version:   go1.7.5
   Git commit:   89658be
   Built:        Thu May  4 22:06:06 2017
   OS/Arch:      linux/amd64
  
  Server:
   Version:      17.05.0-ce
   API version:  1.29 (minimum version 1.12)
   Go version:   go1.7.5
   Git commit:   89658be
   Built:        Thu May  4 22:06:06 2017
   OS/Arch:      linux/amd64
   Experimental: false
  ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `docker/utils/ports.py`
  Content:
  ```
  1 import re
  2 
  3 PORT_SPEC = re.compile(
  4     "^"  # Match full string
  5     "("  # External part
  6     "((?P<host>[a-fA-F\d.:]+):)?"  # Address
  7     "(?P<ext>[\d]*)(-(?P<ext_end>[\d]+))?:"  # External range
  8     ")?"
  9     "(?P<int>[\d]+)(-(?P<int_end>[\d]+))?"  # Internal range
  10     "(?P<proto>/(udp|tcp))?"  # Protocol
  11     "$"  # Match full string
  12 )
  13 
  14 
  15 def add_port_mapping(port_bindings, internal_port, external):
  16     if internal_port in port_bindings:
  17         port_bindings[internal_port].append(external)
  18     else:
  19         port_bindings[internal_port] = [external]
  20 
  21 
  22 def add_port(port_bindings, internal_port_range, external_range):
  23     if external_range is None:
  24         for internal_port in internal_port_range:
  25             add_port_mapping(port_bindings, internal_port, None)
  26     else:
  27         ports = zip(internal_port_range, external_range)
  28         for internal_port, external_port in ports:
  29             add_port_mapping(port_bindings, internal_port, external_port)
  30 
  31 
  32 def build_port_bindings(ports):
  33     port_bindings = {}
  34     for port in ports:
  35         internal_port_range, external_range = split_port(port)
  36         add_port(port_bindings, internal_port_range, external_range)
  37     return port_bindings
  38 
  39 
  40 def _raise_invalid_port(port):
  41     raise ValueError('Invalid port "%s", should be '
  42                      '[[remote_ip:]remote_port[-remote_port]:]'
  43                      'port[/protocol]' % port)
  44 
  45 
  46 def port_range(start, end, proto, randomly_available_port=False):
  47     if not start:
  48         return start
  49     if not end:
  50         return [start + proto]
  51     if randomly_available_port:
  52         return ['{}-{}'.format(start, end) + proto]
  53     return [str(port) + proto for port in range(int(start), int(end) + 1)]
  54 
  55 
  56 def split_port(port):
  57     port = str(port)
  58     match = PORT_SPEC.match(port)
  59     if match is None:
  60         _raise_invalid_port(port)
  61     parts = match.groupdict()
  62 
  63     host = parts['host']
  64     proto = parts['proto'] or ''
  65     internal = port_range(parts['int'], parts['int_end'], proto)
  66     external = port_range(
  67         parts['ext'], parts['ext_end'], '', len(internal) == 1)
  68 
  69     if host is None:
  70         if external is not None and len(internal) != len(external):
  71             raise ValueError('Port ranges don\'t match in length')
  72         return internal, external
  73     else:
  74         if not external:
  75             external = [None] * len(internal)
  76         elif len(internal) != len(external):
  77             raise ValueError('Port ranges don\'t match in length')
  78         return internal, [(host, ext_port) for ext_port in external]
  79
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/docker/utils/ports.py b/docker/utils/ports.py
  --- a/docker/utils/ports.py
  +++ b/docker/utils/ports.py
  @@ -54,6 +54,11 @@
   
   
   def split_port(port):
  +    if hasattr(port, 'legacy_repr'):
  +        # This is the worst hack, but it prevents a bug in Compose 1.14.0
  +        # https://github.com/docker/docker-py/issues/1668
  +        # TODO: remove once fixed in Compose stable
  +        port = port.legacy_repr()
       port = str(port)
       match = PORT_SPEC.match(port)
       if match is None:
  
verification_info: {"golden_diff": "diff --git a/docker/utils/ports.py b/docker/utils/ports.py\n--- a/docker/utils/ports.py\n+++ b/docker/utils/ports.py\n@@ -54,6 +54,11 @@\n \n \n def split_port(port):\n+    if hasattr(port, 'legacy_repr'):\n+        # This is the worst hack, but it prevents a bug in Compose 1.14.0\n+        # https://github.com/docker/docker-py/issues/1668\n+        # TODO: remove once fixed in Compose stable\n+        port = port.legacy_repr()\n     port = str(port)\n     match = PORT_SPEC.match(port)\n     if match is None:\n", "issue": "Issue with port option in 2.4.0 version\nHi,\r\nI update to the 2.4 today and i got issue with docker-compose when i try to add the following line to my configuration file (docker-compose.yml) : \r\n`ports:\r\n            - \"127.0.0.1:9292:9090\"`\r\n\r\nI got the following error:\r\n\r\n`\r\nERROR: for ContainerName  expected string or buffer\r\nTraceback (most recent call last):\r\n  File \"/usr/local/bin/docker-compose\", line 11, in <module>\r\n    sys.exit(main())\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/cli/main.py\", line 68, in main\r\n    command()\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/cli/main.py\", line 118, in perform_command\r\n    handler(command, command_options)\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/cli/main.py\", line 926, in up\r\n    scale_override=parse_scale_args(options['--scale']),\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/project.py\", line 424, in up\r\n    get_deps\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/parallel.py\", line 69, in parallel_execute\r\n    raise error_to_reraise\r\nTypeError: expected string or buffer\r\n`\r\n\r\nI have no issue when i downgrade again to the 2.3 version of the package\r\n\r\nTo reproduce the issue, i use the following configuration ( it doesn't seem to depend on the image):\r\n```\r\nversion: '2'\r\n\r\nservices :\r\n    ContainerName:\r\n        image: bae2d441e03a\r\n        ports:\r\n            - \"127.0.0.1:9292:9090\"\r\n```\r\n\r\nI run on Ubuntu 14.04.5 LTS with the following package:\r\n```\r\ndocker==2.4.0\r\ndocker-compose==1.14.0\r\ndocker-pycreds==0.2.1\r\ndockerpty==0.4.1\r\nPython 2.7.6\r\nClient:\r\n Version:      17.05.0-ce\r\n API version:  1.29\r\n Go version:   go1.7.5\r\n Git commit:   89658be\r\n Built:        Thu May  4 22:06:06 2017\r\n OS/Arch:      linux/amd64\r\n\r\nServer:\r\n Version:      17.05.0-ce\r\n API version:  1.29 (minimum version 1.12)\r\n Go version:   go1.7.5\r\n Git commit:   89658be\r\n Built:        Thu May  4 22:06:06 2017\r\n OS/Arch:      linux/amd64\r\n Experimental: false\r\n```\n", "before_files": [{"content": "import re\n\nPORT_SPEC = re.compile(\n    \"^\"  # Match full string\n    \"(\"  # External part\n    \"((?P<host>[a-fA-F\\d.:]+):)?\"  # Address\n    \"(?P<ext>[\\d]*)(-(?P<ext_end>[\\d]+))?:\"  # External range\n    \")?\"\n    \"(?P<int>[\\d]+)(-(?P<int_end>[\\d]+))?\"  # Internal range\n    \"(?P<proto>/(udp|tcp))?\"  # Protocol\n    \"$\"  # Match full string\n)\n\n\ndef add_port_mapping(port_bindings, internal_port, external):\n    if internal_port in port_bindings:\n        port_bindings[internal_port].append(external)\n    else:\n        port_bindings[internal_port] = [external]\n\n\ndef add_port(port_bindings, internal_port_range, external_range):\n    if external_range is None:\n        for internal_port in internal_port_range:\n            add_port_mapping(port_bindings, internal_port, None)\n    else:\n        ports = zip(internal_port_range, external_range)\n        for internal_port, external_port in ports:\n            add_port_mapping(port_bindings, internal_port, external_port)\n\n\ndef build_port_bindings(ports):\n    port_bindings = {}\n    for port in ports:\n        internal_port_range, external_range = split_port(port)\n        add_port(port_bindings, internal_port_range, external_range)\n    return port_bindings\n\n\ndef _raise_invalid_port(port):\n    raise ValueError('Invalid port \"%s\", should be '\n                     '[[remote_ip:]remote_port[-remote_port]:]'\n                     'port[/protocol]' % port)\n\n\ndef port_range(start, end, proto, randomly_available_port=False):\n    if not start:\n        return start\n    if not end:\n        return [start + proto]\n    if randomly_available_port:\n        return ['{}-{}'.format(start, end) + proto]\n    return [str(port) + proto for port in range(int(start), int(end) + 1)]\n\n\ndef split_port(port):\n    port = str(port)\n    match = PORT_SPEC.match(port)\n    if match is None:\n        _raise_invalid_port(port)\n    parts = match.groupdict()\n\n    host = parts['host']\n    proto = parts['proto'] or ''\n    internal = port_range(parts['int'], parts['int_end'], proto)\n    external = port_range(\n        parts['ext'], parts['ext_end'], '', len(internal) == 1)\n\n    if host is None:\n        if external is not None and len(internal) != len(external):\n            raise ValueError('Port ranges don\\'t match in length')\n        return internal, external\n    else:\n        if not external:\n            external = [None] * len(internal)\n        elif len(internal) != len(external):\n            raise ValueError('Port ranges don\\'t match in length')\n        return internal, [(host, ext_port) for ext_port in external]\n", "path": "docker/utils/ports.py"}], "after_files": [{"content": "import re\n\nPORT_SPEC = re.compile(\n    \"^\"  # Match full string\n    \"(\"  # External part\n    \"((?P<host>[a-fA-F\\d.:]+):)?\"  # Address\n    \"(?P<ext>[\\d]*)(-(?P<ext_end>[\\d]+))?:\"  # External range\n    \")?\"\n    \"(?P<int>[\\d]+)(-(?P<int_end>[\\d]+))?\"  # Internal range\n    \"(?P<proto>/(udp|tcp))?\"  # Protocol\n    \"$\"  # Match full string\n)\n\n\ndef add_port_mapping(port_bindings, internal_port, external):\n    if internal_port in port_bindings:\n        port_bindings[internal_port].append(external)\n    else:\n        port_bindings[internal_port] = [external]\n\n\ndef add_port(port_bindings, internal_port_range, external_range):\n    if external_range is None:\n        for internal_port in internal_port_range:\n            add_port_mapping(port_bindings, internal_port, None)\n    else:\n        ports = zip(internal_port_range, external_range)\n        for internal_port, external_port in ports:\n            add_port_mapping(port_bindings, internal_port, external_port)\n\n\ndef build_port_bindings(ports):\n    port_bindings = {}\n    for port in ports:\n        internal_port_range, external_range = split_port(port)\n        add_port(port_bindings, internal_port_range, external_range)\n    return port_bindings\n\n\ndef _raise_invalid_port(port):\n    raise ValueError('Invalid port \"%s\", should be '\n                     '[[remote_ip:]remote_port[-remote_port]:]'\n                     'port[/protocol]' % port)\n\n\ndef port_range(start, end, proto, randomly_available_port=False):\n    if not start:\n        return start\n    if not end:\n        return [start + proto]\n    if randomly_available_port:\n        return ['{}-{}'.format(start, end) + proto]\n    return [str(port) + proto for port in range(int(start), int(end) + 1)]\n\n\ndef split_port(port):\n    if hasattr(port, 'legacy_repr'):\n        # This is the worst hack, but it prevents a bug in Compose 1.14.0\n        # https://github.com/docker/docker-py/issues/1668\n        # TODO: remove once fixed in Compose stable\n        port = port.legacy_repr()\n    port = str(port)\n    match = PORT_SPEC.match(port)\n    if match is None:\n        _raise_invalid_port(port)\n    parts = match.groupdict()\n\n    host = parts['host']\n    proto = parts['proto'] or ''\n    internal = port_range(parts['int'], parts['int_end'], proto)\n    external = port_range(\n        parts['ext'], parts['ext_end'], '', len(internal) == 1)\n\n    if host is None:\n        if external is not None and len(internal) != len(external):\n            raise ValueError('Port ranges don\\'t match in length')\n        return internal, external\n    else:\n        if not external:\n            external = [None] * len(internal)\n        elif len(internal) != len(external):\n            raise ValueError('Port ranges don\\'t match in length')\n        return internal, [(host, ext_port) for ext_port in external]\n", "path": "docker/utils/ports.py"}]}