Repository: localstack/localstack

Issue:
bug: Starting localstack fails for python 3.7.4
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

After a clean install using `pip install localstack`, running `localstack start` fails by producing the following output:

```

     __                     _______ __             __
    / /   ____  _________ _/ / ___// /_____ ______/ /__
   / /   / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/
  / /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
 /_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_|

 💻 LocalStack CLI 0.14.2.11

Traceback (most recent call last):
  File "/home/phii/Code/aws-test/.venv/bin/localstack", line 23, in <module>
    main()
  File "/home/phii/Code/aws-test/.venv/bin/localstack", line 19, in main
    main.main()
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/cli/main.py", line 6, in main
    cli()
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/cli/plugin.py", line 15, in __call__
    self.group(*args, **kwargs)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/cli/localstack.py", line 125, in cmd_start
    from localstack.utils import bootstrap
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/utils/bootstrap.py", line 16, in <module>
    from localstack.utils.container_networking import get_main_container_name
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/utils/container_networking.py", line 7, in <module>
    from localstack.utils.container_utils.container_client import ContainerException
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/utils/container_utils/container_client.py", line 13, in <module>
    from typing import Dict, List, Optional, Protocol, Tuple, Union
ImportError: cannot import name 'Protocol' from 'typing' (/home/phii/.pyenv/versions/3.7.4/lib/python3.7/typing.py)
```

As you can see from the logs, I am using python 3.7.4.
After googling the python docs, I saw that "Protocol" wasn't added to the "typing" module until subsequent python versions, however the docs clearly state that localstack supports python 3.7.

### Expected Behavior

I expected for localstack to start normally.

### How are you starting LocalStack?

Custom (please describe below)

### Steps To Reproduce

#### How are you starting localstack (e.g., `bin/localstack` command, arguments, or `docker-compose.yml`)

`bin/localstack`

#### Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)

`localstack start`

### Environment

```markdown
- OS: ManjaroLinux 21.2.6
- LocalStack: latest
```


Discussion:
Welcome to LocalStack! Thanks for reporting your first issue and our team will be working towards fixing the issue for you or reach out for more background information. We recommend joining our [Slack Community](https://localstack.cloud/contact/) for real-time help and drop a message to LocalStack Pro Support if you are a Pro user! If you are willing to contribute towards fixing this issue, please have a look at our [contributing guidelines](https://github.com/localstack/.github/blob/main/CONTRIBUTING.md) and our [developer guide](https://docs.localstack.cloud/developer-guide/).
BTW: The latest compatible version that doesn't crash when I call `localstack start` is `localstack 0.14.2.1`.

Resolving patch:
diff --git a/localstack/utils/collections.py b/localstack/utils/collections.py
--- a/localstack/utils/collections.py
+++ b/localstack/utils/collections.py
@@ -3,19 +3,14 @@
 
 import logging
 import re
-from typing import (
-    Any,
-    Callable,
-    Dict,
-    List,
-    Optional,
-    Sized,
-    Tuple,
-    Type,
-    TypedDict,
-    TypeVar,
-    Union,
-)
+import sys
+from typing import Any, Callable, Dict, List, Optional, Sized, Tuple, Type, TypeVar, Union
+
+if sys.version_info >= (3, 8):
+    from typing import TypedDict
+else:
+    from typing_extensions import TypedDict
+
 
 LOG = logging.getLogger(__name__)
 
diff --git a/localstack/utils/container_networking.py b/localstack/utils/container_networking.py
--- a/localstack/utils/container_networking.py
+++ b/localstack/utils/container_networking.py
@@ -10,7 +10,7 @@
 LOG = logging.getLogger(__name__)
 
 
-@lru_cache
+@lru_cache()
 def get_main_container_network() -> Optional[str]:
     """
     Gets the main network of the LocalStack container (if we run in one, bridge otherwise)
@@ -32,7 +32,7 @@ def get_main_container_network() -> Optional[str]:
     return main_container_network
 
 
-@lru_cache
+@lru_cache()
 def get_endpoint_for_network(network: Optional[str] = None) -> str:
     """
     Get the LocalStack endpoint (= IP address) on the given network.
@@ -90,7 +90,7 @@ def get_main_container_id():
         return None
 
 
-@lru_cache
+@lru_cache()
 def get_main_container_name():
     """
     Returns the container name of the LocalStack container
diff --git a/localstack/utils/container_utils/container_client.py b/localstack/utils/container_utils/container_client.py
--- a/localstack/utils/container_utils/container_client.py
+++ b/localstack/utils/container_utils/container_client.py
@@ -5,12 +5,18 @@
 import os
 import re
 import shlex
+import sys
 import tarfile
 import tempfile
 from abc import ABCMeta, abstractmethod
 from enum import Enum, unique
 from pathlib import Path
-from typing import Dict, List, Optional, Protocol, Tuple, Union
+from typing import Dict, List, Optional, Tuple, Union
+
+if sys.version_info >= (3, 8):
+    from typing import Protocol
+else:
+    from typing_extensions import Protocol
 
 from localstack import config
 from localstack.utils.collections import HashableList


Test patch: