0

I have this code for a function to populate a connection string for sqlalchemy

def sql_path(yaml_path=None, login_key='login', user_key='username',
             pass_key='password', api_key=None, dialect=None,
             driver=None, host='localhost', port=None, database=None):
    
    with open(yaml_path, 'r') as file:
        sql_yaml = yaml.load(file, Loader=yaml.FullLoader)
    user = sql_yaml[login_key][user_key]
    password = sql_yaml[login_key][pass_key]
    pw_encoded = urllib.parse.quote_plus(password)
    if api_key is not None:
        if 'dialect' in sql_yaml[api_key].keys():
            dialect = sql_yaml[api_key]['dialect']
        if 'driver' in sql_yaml[api_key].keys():
            driver = sql_yaml[api_key]['driver']
        if 'host' in sql_yaml[api_key].keys():
            host = sql_yaml[api_key]['host']
        if 'port' in sql_yaml[api_key].keys():
            port = sql_yaml[api_key]['port']
        if 'database' in sql_yaml[api_key].keys():
            database = sql_yaml[api_key]['database']

    if port is None:
        sql_path = f'{dialect}+{driver}://{user}:{pw_encoded}@{host}/{database}'
    else:
        sql_path = f'{dialect}+{driver}://{user}:{pw_encoded}@{host}:{port}/{database}'
    
    return sql_path

Is there a way to dynamically assign the variables in the 'if api_key...' block without having to do an if statement for each of them?

  • 1
    Why use separate variables at all? – Charles Duffy Dec 21 '20 at 23:41
  • 1
    For example, if you use `v = sql_yaml[api_key]`, you can have `sql_path = f'{v["dialect"]}+{v["driver"]}://{v["user"]}:...` – Charles Duffy Dec 21 '20 at 23:42
  • Also, instead of using the `if` statements at all, just use `get`. – Charles Duffy Dec 21 '20 at 23:42
  • 1
    That would make it, for example, `dialect = sql_yaml[api_key].get('dialect', dialect)`, with no `if` needed, if you _do_ want to continue using separate variables. – Charles Duffy Dec 21 '20 at 23:43
  • Granted, it _is_ possible to update your locals by merging a dict into them; I just don't see a compelling need for it -- it makes static checking tools unable to handle your code, so it's something that should only be done when you have a much more compelling reason than just a little bit of terseness. – Charles Duffy Dec 21 '20 at 23:44
  • No, in CPython, you cannot dynamically modify local variables. You really don't need to. *Just use a dictionary*, or a `SimpleNamespace` if you prefer – juanpa.arrivillaga Dec 22 '20 at 00:03
  • @CharlesDuffy thank you for the ideas! I see your point with just assigning the dictionary key and then calling it in the f-string. I think I'm going to go with the second suggestion. That way the variable can still be assigned from the keywords if they are not in the yaml file. – Kelly Shortell Dec 22 '20 at 03:35

1 Answers1

1

Use get which will default to None if the key is not found

Replace

if api_key is not None:
    if 'dialect' in sql_yaml[api_key].keys():
        dialect = sql_yaml[api_key]['dialect']
    if 'driver' in sql_yaml[api_key].keys():
        driver = sql_yaml[api_key]['driver']
    if 'host' in sql_yaml[api_key].keys():
        host = sql_yaml[api_key]['host']
    if 'port' in sql_yaml[api_key].keys():
        port = sql_yaml[api_key]['port']
    if 'database' in sql_yaml[api_key].keys():
        database = sql_yaml[api_key]['database']

with

if api_key:
    dialect = sql_yaml[api_key].get('dialect')
    driver = sql_yaml[api_key].get('driver')
    host = sql_yaml[api_key].get('host')
    port = sql_yaml[api_key].get('port')
    database = sql_yaml[api_key].get('database')

OR with

if api_key:
    dialect,  driver,  host,  port,  database = map(sql_yaml[api_key].get, ['dialect', 'driver', 'host', 'port', 'database'])
Aaj Kaal
  • 1,205
  • 1
  • 9
  • 8
  • Thank you for the post. I went with your suggestion and added the second value, so it could be assigned by keyword if it doesn't appear in the yaml file. – Kelly Shortell Dec 22 '20 at 03:38