Repository: techservicesillinois/awscli-login

Issue:
Potential dependency conflicts between awscli-login and botocore
Hi, as shown in the following full dependency graph of  **_awscli-login_**, **_awscli-login_** requires **_botocore  *_** ， while the installed version of **_boto3_**(1.10.23) requires _**botocore >=1.13.23,<1.14.0**_.

According to Pip's _“first found wins”_ installation strategy, _**botocore 1.13.23**_ is the actually installed version.  

Although the first found package version _**botocore 1.13.23**_ just satisfies the later dependency constraint （_**botocore >=1.13.23,<1.14.0**_), it will lead to a build failure once developers release a newer version of botocore.

### Dependency tree--------
```
awscli-login - 0.1.0a6
| +- awscli(install version:1.16.287 version range:*)
| +- boto3(install version:1.10.23 version range:*)
| | +- botocore(install version:1.13.23 version range:>=1.13.23,<1.14.0)
| | | +- docutils(install version:0.15.2 version range:>=0.10,<0.16)
| | | +- jmespath(install version:0.9.4 version range:<1.0.0,>=0.7.1)
| | +- jmespath(install version:0.9.4 version range:<1.0.0,>=0.7.1)
| | +- s3transfer(install version:0.2.1 version range:>=0.2.0,<0.3.0)
| | | +- botocore(install version:1.13.23 version range:<2.0.0,>=1.12.36)
| | | | +- docutils(install version:0.15.2 version range:>=0.10,<0.16)
| | | | +- jmespath(install version:0.9.4 version range:<1.0.0,>=0.7.1)
| +- botocore(install version:1.13.23 version range:*)
| | +- docutils(install version:0.15.2 version range:>=0.10,<0.16)
| | +- jmespath(install version:0.9.4 version range:<1.0.0,>=0.7.1)
| +- daemoniker(install version:0.2.3 version range:*)
| +- keyring(install version:19.2.0 version range:*)
| +- lxml(install version:4.4.1 version range:*)
| | +- cython(install version:0.29.14 version range:>=0.29.7)
| +- psutil(install version:5.6.7 version range:*)
| +- requests(install version:2.22.0 version range:*)
| | +- certifi(install version:2019.9.11 version range:>=2017.4.17)
| | +- chardet(install version:3.0.4 version range:<3.1.0,>=3.0.2)
| | +- idna(install version:2.8 version range:>=2.5,<2.9)
| | +- urllib3(install version:1.25.7 version range:<1.26,>=1.21.1) 
```

Thanks for your attention.
Best,
Neolith

FEATURE REQUEST: support for awscli v2
Someone on our campus pointed out that awscli v2 is available and that this plugin does not (yet?) work with it..

https://aws.amazon.com/blogs/developer/aws-cli-v2-is-now-generally-available/

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1e68f17..a6fe909 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,10 @@ Changed
 
 * invalid selection now issues a fatal error message `#71`_
 
+Fixed
+`````
+* drop boto3 dependency that conflicts with awscliv2 `#73`_
+
 `0.2b1`_ 2021-02-04
 ---------------------
 
@@ -87,3 +91,4 @@ Fixed
 .. _#64: https://github.com/techservicesillinois/awscli-login/pull/64
 .. _#66: https://github.com/techservicesillinois/awscli-login/pull/66
 .. _#71: https://github.com/techservicesillinois/awscli-login/pull/71
+.. _#73: https://github.com/techservicesillinois/awscli-login/pull/73
diff --git a/setup.py b/setup.py
index 63e877a..ec7d085 100644
--- a/setup.py
+++ b/setup.py
@@ -51,7 +51,6 @@ setup(
     python_requires='>=3.5',
     install_requires=[
        'awscli',
-       'boto3',
        'botocore',
        'daemoniker',
        'keyring',
diff --git a/src/awscli_login/__main__.py b/src/awscli_login/__main__.py
index 107e37b..2dfad59 100755
--- a/src/awscli_login/__main__.py
+++ b/src/awscli_login/__main__.py
@@ -9,9 +9,8 @@ from datetime import datetime
 from functools import wraps
 from typing import Optional
 
-import boto3
-
 from botocore.session import Session
+from botocore import client as Client
 from daemoniker import Daemonizer, SignalHandler1
 from daemoniker import send, SIGINT, SIGTERM, SIGABRT
 
@@ -44,7 +43,7 @@ from .typing import Role
 logger = logging.getLogger(__package__)
 
 
-def save_sts_token(session: Session, client: boto3.client, saml: str,
+def save_sts_token(session: Session, client: Client, saml: str,
                    role: Role, duration: int = 0) -> datetime:
     params = dict(
         RoleArn=role[1],
@@ -64,7 +63,7 @@ def save_sts_token(session: Session, client: boto3.client, saml: str,
     return save_credentials(session, token)
 
 
-def daemonize(profile: Profile, session: Session, client: boto3.client,
+def daemonize(profile: Profile, session: Session, client: Client,
               role: Role, expires: datetime) -> bool:
     with Daemonizer() as (is_setup, daemonizer):
         is_parent, profile, session, client, role, expires = daemonizer(
@@ -179,7 +178,7 @@ def main(profile: Profile, session: Session):
         logger.warn("Logged out: ignoring --force-refresh.")
 
     try:
-        client = boto3.client('sts')
+        client = session.create_client('sts')
 
         # Exit if already logged in
         profile.raise_if_logged_in()
diff --git a/src/awscli_login/util.py b/src/awscli_login/util.py
index 465c721..d011ebd 100644
--- a/src/awscli_login/util.py
+++ b/src/awscli_login/util.py
@@ -7,6 +7,7 @@ from time import sleep
 from typing import Dict, List, Tuple
 
 from awscli.customizations.configure.set import ConfigureSetCommand
+from botocore.utils import parse_timestamp
 from botocore.session import Session
 
 from .const import ERROR_INVALID_PROFILE_ROLE
@@ -130,9 +131,7 @@ def save_credentials(session: Session, token: Dict) -> datetime:
     _aws_set(session, 'aws_security_token',  creds['SessionToken'])
     logger.info("Saved temporary STS credentials to profile: " + profile)
 
-    assert isinstance(creds['Expiration'], datetime), \
-        "Amazon returned bad Expiration!"
-    return creds['Expiration']
+    return parse_timestamp(creds['Expiration'])
 
 
 def file2bytes(filename: str) -> bytes:


Test patch:
diff --git a/src/tests/test_util.py b/src/tests/test_util.py
index 467c64f..2fa746c 100755
--- a/src/tests/test_util.py
+++ b/src/tests/test_util.py
@@ -1,7 +1,6 @@
 import os
 import unittest
 
-from datetime import datetime, timezone
 from io import StringIO
 from multiprocessing import get_start_method
 from os.path import isfile
@@ -33,7 +32,7 @@ def token(akey: str, skey: str, stoken: str) -> Dict[str, Dict[str, Any]]:
                                   'AccessKeyId': akey,
                                   'SecretAccessKey': skey,
                                   'SessionToken': stoken,
-                                  'Expiration': datetime.now(timezone.utc)
+                                  'Expiration': '2021-02-11T00:42:09Z'
                               }
            }