Skip to content

Commit dd1c2f1

Browse files
Option to specify profile
1 parent 8cb9b23 commit dd1c2f1

File tree

2 files changed

+37
-13
lines changed

2 files changed

+37
-13
lines changed

S3/Config.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -261,18 +261,19 @@ class Config(object):
261261
max_retries = 5
262262

263263
## Creating a singleton
264-
def __new__(self, configfile = None, access_key=None, secret_key=None, access_token=None):
264+
def __new__(self, configfile = None, access_key=None, secret_key=None, access_token=None, profile=None):
265265
if self._instance is None:
266266
self._instance = object.__new__(self)
267267
return self._instance
268268

269-
def __init__(self, configfile = None, access_key=None, secret_key=None, access_token=None):
269+
def __init__(self, configfile = None, access_key=None, secret_key=None, access_token=None, profile=None):
270270
if configfile:
271+
debug("Config: Initializing with config file '%s', profile '%s'" % (configfile, profile))
271272
try:
272-
self.read_config_file(configfile)
273+
self.read_config_file(configfile, profile)
273274
except IOError:
274275
if 'AWS_SHARED_CREDENTIALS_FILE' in os.environ or 'AWS_CREDENTIAL_FILE' in os.environ or 'AWS_PROFILE' in os.environ:
275-
self.aws_credential_file()
276+
self.aws_credential_file(profile)
276277

277278
# override these if passed on the command-line
278279
# Allow blank secret_key
@@ -335,7 +336,7 @@ def role_config(self):
335336
'%s=%s' % (k, s3_quote(v, unicode_output=True))
336337
for k, v in params.items()
337338
])
338-
sts_endpoint = os.environ.get("AWS_STS_ENDPOINT", sts_endpoint)
339+
sts_endpoint = os.environ.get("AWS_STS_ENDPOINT", sts_endpoint)
339340
if os.environ.get("AWS_STS_REGIONAL_ENDPOINTS") == "regional":
340341
# Check if the AWS_REGION variable is available to use as a region.
341342
region = os.environ.get("AWS_REGION")
@@ -441,7 +442,7 @@ def role_refresh(self):
441442
except Exception:
442443
warning("Could not refresh role")
443444

444-
def aws_credential_file(self):
445+
def aws_credential_file(self, profile=None):
445446
try:
446447
aws_credential_file = os.path.expanduser('~/.aws/credentials')
447448
credential_file_from_env = os.environ.get('AWS_SHARED_CREDENTIALS_FILE') \
@@ -450,6 +451,7 @@ def aws_credential_file(self):
450451
os.path.isfile(credential_file_from_env):
451452
aws_credential_file = base_unicodise(credential_file_from_env)
452453
elif not os.path.isfile(aws_credential_file):
454+
debug("AWS credentials file not found at %s" % aws_credential_file)
453455
return
454456

455457
config = PyConfigParser()
@@ -482,7 +484,8 @@ def aws_credential_file(self):
482484
"Error reading aws_credential_file "
483485
"(%s): %s" % (aws_credential_file, str(exc)))
484486

485-
profile = base_unicodise(os.environ.get('AWS_PROFILE', "default"))
487+
# Use the profile from the parameter if provided, otherwise use the environment variable or default
488+
profile = profile or base_unicodise(os.environ.get('AWS_PROFILE', "default"))
486489
debug("Using AWS profile '%s'" % (profile))
487490

488491
# get_key - helper function to read the aws profile credentials
@@ -559,8 +562,16 @@ def option_list(self):
559562
retval.append(option)
560563
return retval
561564

562-
def read_config_file(self, configfile):
563-
cp = ConfigParser(configfile)
565+
def read_config_file(self, configfile, profile=None):
566+
# If profile is specified, use it as the section name
567+
sections = [profile] if profile else []
568+
cp = ConfigParser(configfile, sections)
569+
570+
# If no profile is specified or the profile section doesn't exist,
571+
# fall back to the default section
572+
if not profile or not cp.cfg:
573+
cp = ConfigParser(configfile, [])
574+
564575
for option in self.option_list():
565576
_option = cp.get(option)
566577
if _option is not None:
@@ -655,20 +666,29 @@ def parse_file(self, file, sections = []):
655666
debug("ConfigParser: Reading file '%s'" % file)
656667
if type(sections) != type([]):
657668
sections = [sections]
658-
in_our_section = True
669+
670+
if sections:
671+
debug("ConfigParser: Looking for sections: %s" % sections)
672+
else:
673+
debug("ConfigParser: Reading default section")
674+
675+
in_our_section = len(sections) == 0 # If no sections specified, read all
676+
current_section = "default"
659677
r_comment = re.compile(r'^\s*#.*')
660678
r_empty = re.compile(r'^\s*$')
661679
r_section = re.compile(r'^\[([^\]]+)\]')
662680
r_data = re.compile(r'^\s*(?P<key>\w+)\s*=\s*(?P<value>.*)')
663681
r_quotes = re.compile(r'^"(.*)"\s*$')
682+
664683
with io.open(file, "r", encoding=self.get('encoding', 'UTF-8')) as fp:
665684
for line in fp:
666685
if r_comment.match(line) or r_empty.match(line):
667686
continue
668687
is_section = r_section.match(line)
669688
if is_section:
670-
section = is_section.groups()[0]
671-
in_our_section = (section in sections) or (len(sections) == 0)
689+
current_section = is_section.groups()[0]
690+
in_our_section = (current_section in sections) or (len(sections) == 0)
691+
debug("ConfigParser: Found section '%s', reading: %s" % (current_section, in_our_section))
672692
continue
673693
is_data = r_data.match(line)
674694
if is_data and in_our_section:
@@ -682,6 +702,9 @@ def parse_file(self, file, sections = []):
682702
print_value = data["value"]
683703
debug("ConfigParser: %s->%s" % (data["key"], print_value))
684704
continue
705+
if is_data and not in_our_section:
706+
# Skip data in sections we're not interested in
707+
continue
685708
warning("Ignoring invalid line in '%s': %s" % (file, line))
686709

687710
def __getitem__(self, name):

s3cmd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3158,6 +3158,7 @@ def main():
31583158
optparser.add_option( "--access_key", dest="access_key", help="AWS Access Key")
31593159
optparser.add_option( "--secret_key", dest="secret_key", help="AWS Secret Key")
31603160
optparser.add_option( "--access_token", dest="access_token", help="AWS Access Token")
3161+
optparser.add_option( "--profile", dest="profile", metavar="PROFILE", help="Profile to use from .s3cfg file")
31613162

31623163
optparser.add_option("-n", "--dry-run", dest="dry_run", action="store_true", help="Only show what should be uploaded or downloaded but don't actually do it. May still perform S3 requests to get bucket listings and other information though (only for file transfer commands)")
31633164

@@ -3316,7 +3317,7 @@ def main():
33163317
sys.exit(EX_CONFIG)
33173318

33183319
try:
3319-
cfg = Config(options.config, options.access_key, options.secret_key, options.access_token)
3320+
cfg = Config(options.config, options.access_key, options.secret_key, options.access_token, options.profile)
33203321
except ValueError as exc:
33213322
raise ParameterError(unicode(exc))
33223323
except IOError as e:

0 commit comments

Comments
 (0)