Gestion de la configuration

Les applications ont besoin d’une certaine forme de configuration. Il existe différents paramètres que vous pouvez vouloir modifier en fonction de l’environnement de l’application, comme l’activation du mode de débogage, la définition de la clé secrète et d’autres éléments spécifiques à l’environnement.

La façon dont Flask est conçu nécessite généralement que la configuration soit disponible au démarrage de l’application. Vous pouvez coder en dur la configuration dans le code, ce qui, pour de nombreuses petites applications, n’est pas si mal, mais il existe de meilleures méthodes.

Indépendamment de la façon dont vous chargez votre configuration, il y a un objet config disponible qui contient les valeurs de la configuration chargée: l’attribut config de l’objet Flask. C’est l’endroit où Flask lui-même met certaines valeurs de configuration et aussi où les extensions peuvent mettre leurs valeurs de configuration. Mais c’est aussi l’endroit où vous pouvez avoir votre propre configuration.

Principes de base de la configuration

La config est en fait une sous-classe d’un dictionnaire et peut être modifiée comme tout dictionnaire:

app = Flask(__name__)
app.config['TESTING'] = True

Certaines valeurs de configuration sont également transmises à l’objet Flask afin que vous puissiez les lire et les écrire à partir de là:

app.testing = True

Pour mettre à jour plusieurs clés à la fois, vous pouvez utiliser la méthode dict.update():

app.config.update(
    TESTING=True,
    SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
)

Environnement et fonctions de débogage

Les valeurs de configuration ENV et DEBUG sont spéciales car elles peuvent se comporter de manière incohérente si elles sont modifiées après le début de l’installation de l’application. Afin de définir l’environnement et le mode de débogage de manière fiable, Flask utilise des variables d’environnement.

L’environnement est utilisé pour indiquer à Flask, aux extensions et aux autres programmes, comme Sentry, dans quel contexte Flask est exécuté. Il est contrôlé par la variable d’environnement FLASK_ENV et a pour valeur par défaut production.

Définir FLASK_ENV à développement activera le mode débogage. flask run utilisera le débogueur interactif et le reloader par défaut en mode débogage. Pour contrôler cela séparément de l’environnement, utilisez l’option FLASK_DEBUG.

Changelog

Modifié dans la version 1.0: Ajout de FLASK_ENV pour contrôler l’environnement séparément du mode de débogage. L’environnement de développement active le mode de débogage.

Pour basculer Flask dans l’environnement de développement et activer le mode débogage, définissez FLASK_ENV :

$ export FLASK_ENV=development
$ flask run

Il est recommandé d’utiliser les variables d’environnement comme décrit ci-dessus. Bien qu’il soit possible de définir ENV et DEBUG dans votre configuration ou votre code, cela est fortement déconseillé. Elles ne peuvent pas être lues précocement par la commande flask, et certains systèmes ou extensions peuvent s’être déjà configurés sur la base d’une valeur précédente.

Valeurs de configuration intégrées

Les valeurs de configuration suivantes sont utilisées en interne par Flask :

ENV

L’environnement dans lequel l’application est exécutée. Flask et les extensions peuvent activer des comportements basés sur l’environnement, comme l’activation du mode débogage. L’attribut env correspond à cette clé de configuration. Elle est définie par la variable d’environnement FLASK_ENV et peut ne pas se comporter comme prévu si elle est définie dans le code.

Ne pas activer le mode développement lors du déploiement en production.

Valeur par défaut : production

Changelog

Nouveau dans la version 1.0.

DEBUG

Si le mode de débogage est activé. Lorsque vous utilisez flask run pour démarrer le serveur de développement, un débogueur interactif sera affiché pour les exceptions non gérées, et le serveur sera rechargé lorsque le code sera modifié. L’attribut debug correspond à cette clé de configuration. Ceci est activé lorsque ENV est 'development' et est surchargé par la variable d’environnement FLASK_DEBUG. Elle peut ne pas se comporter comme prévu si elle est définie dans le code.

Ne pas activer le mode débogage lors du déploiement en production.

Valeur par défaut : True si ENV est 'development', ou False sinon.

TESTING

Activez le mode de test. Les exceptions sont propagées plutôt que d’être traitées par les gestionnaires d’erreurs de l’application. Les extensions peuvent également modifier leur comportement pour faciliter les tests. Vous devriez activer ce mode dans vos propres tests.

Valeur par défaut : False

PROPAGATE_EXCEPTIONS

Les exceptions sont re-levées plutôt que d’être gérées par les gestionnaires d’erreurs de l’application. Si elle n’est pas définie, elle est implicitement vraie si TESTING ou DEBUG est activé.

Valeur par défaut : None

PRESERVE_CONTEXT_ON_EXCEPTION

Ne pas ouvrir le contexte de la requête lorsqu’une exception se produit. Si elle n’est pas définie, elle est vraie si DEBUG est vrai. Cela permet aux débogueurs d’introspecter les données de la requête en cas d’erreur, et ne devrait normalement pas avoir besoin d’être défini directement.

Valeur par défaut : None

TRAP_HTTP_EXCEPTIONS

S’il n’y a pas de gestionnaire pour une exception de type HTTPException, relancez-la pour qu’elle soit gérée par le débogueur interactif au lieu de la renvoyer comme une simple réponse d’erreur.

Valeur par défaut : False

TRAP_BAD_REQUEST_ERRORS

Essayer d’accéder à une clé qui n’existe pas à partir des dictionnaires de requêtes comme args et form retournera une page d’erreur 400 Bad Request. Activez cette option pour traiter l’erreur comme une exception non gérée afin d’obtenir le débogueur interactif. C’est une version plus spécifique de TRAP_HTTP_EXCEPTIONS. Si elle n’est pas définie, elle est activée en mode débogage.

Valeur par défaut : None

SECRET_KEY

Une clé secrète qui sera utilisée pour signer de manière sécurisée le cookie de session et qui peut être utilisée pour tout autre besoin lié à la sécurité par les extensions ou votre application. Ce doit être un long bytes ou str aléatoire. Par exemple, copiez la sortie de ceci dans votre config:

$ python -c 'import os; print(os.urandom(16))'
b'_5#y2L"F4Q8z\n\xec]/'

Ne révélez pas la clé secrète lorsque vous posez des questions ou commettez le code.

Valeur par défaut : None

Le nom du cookie de session. Peut être modifié dans le cas où vous avez déjà un cookie avec le même nom.

Valeur par défaut : session

La règle de correspondance de domaine pour laquelle le cookie de session sera valide. Si elle n’est pas définie, le cookie sera valable pour tous les sous-domaines de SERVER_NAME. Si elle est définie sur False, le domaine du cookie ne sera pas défini.

Valeur par défaut : None

Le chemin pour lequel le cookie de session sera valide. S’il n’est pas défini, le cookie sera valide sous APPLICATION_ROOT ou / si celui-ci n’est pas défini.

Valeur par défaut : None

Pour des raisons de sécurité, les navigateurs ne permettent pas à JavaScript d’accéder aux cookies marqués « HTTP uniquement ».

Valeur par défaut : True

Les navigateurs n’enverront des cookies avec les demandes sur HTTPS que si le cookie est marqué « secure ». L’application doit être servie via HTTPS pour que cela ait un sens.

Valeur par défaut : False

Limite la façon dont les cookies sont envoyés avec les requêtes provenant de sites externes. Peut être défini sur 'Lax' (recommandé) ou 'Strict'. Voir Set-Cookie options.

Valeur par défaut : None

Changelog

Nouveau dans la version 1.0.

PERMANENT_SESSION_LIFETIME

Si session.permanent est vrai, l’expiration du cookie sera fixée à ce nombre de secondes dans le futur. Peut être soit un datetime.timedelta ou un int.

L’implémentation par défaut des cookies de Flask valide que la signature cryptographique n’est pas plus ancienne que cette valeur.

Valeur par défaut : timedelta(days=31) (2678400 secondes)

SESSION_REFRESH_EACH_REQUEST

Contrôle si le cookie est envoyé avec chaque réponse lorsque session.permanent est vrai. L’envoi du cookie à chaque fois (par défaut) peut empêcher de façon plus fiable la session d’expirer, mais utilise plus de bande passante. Les sessions non permanentes ne sont pas affectées.

Valeur par défaut : True

USE_X_SENDFILE

Lorsque vous servez des fichiers, définissez l’en-tête X-Sendfile au lieu de servir les données avec Flask. Certains serveurs web, comme Apache, reconnaissent cela et servent les données plus efficacement. Cela n’a de sens que si vous utilisez un tel serveur.

Valeur par défaut : False

SEND_FILE_MAX_AGE_DEFAULT

Lorsque vous servez des fichiers, définissez l’âge maximal du contrôle du cache à ce nombre de secondes. Peut être une datetime.timedelta ou un int. Remplacez cette valeur pour chaque fichier en utilisant get_send_file_max_age() sur l’application ou le blueprint.

Si None, send_file indique au navigateur d’utiliser des requêtes conditionnelles qui seront utilisées au lieu d’un cache temporisé, ce qui est généralement préférable.

Valeur par défaut : None

SERVER_NAME

Indique à l’application à quel hôte et à quel port elle est liée. Nécessaire pour la prise en charge de la correspondance des routes entre les sous-domaines.

S’il est défini, il sera utilisé pour le domaine du cookie de session si SESSION_COOKIE_DOMAIN n’est pas défini. Les navigateurs Web modernes ne permettent pas de définir des cookies pour les domaines sans point. Pour utiliser un domaine localement, ajoutez tous les noms qui doivent être dirigés vers l’application à votre fichier hosts.:

127.0.0.1 localhost.dev

S’il est défini, url_for peut générer des URLs externes avec seulement un contexte d’application au lieu d’un contexte de requête.

Valeur par défaut : None

APPLICATION_ROOT

Informe l’application du chemin sous lequel elle est montée par l’application / le serveur web. Ceci est utilisé pour générer des URL en dehors du contexte d’une requête (à l’intérieur d’une requête, le distributeur est responsable de la définition de SCRIPT_NAME à la place ; voir Application Dispatching pour des exemples de configuration du distributeur).

Sera utilisé pour le chemin du cookie de session si SESSION_COOKIE_PATH n’est pas défini.

Valeur par défaut : '/'

PREFERRED_URL_SCHEME

Utilisez ce schéma pour générer des URL externes lorsqu’elles ne sont pas dans un contexte de demande.

Valeur par défaut : 'http'

MAX_CONTENT_LENGTH

Ne pas lire plus de ce nombre d’octets dans les données de la requête entrante. S’il n’est pas défini et que la requête ne spécifie pas de CONTENT_LENGTH, aucune donnée ne sera lue par sécurité.

Valeur par défaut : None

JSON_AS_ASCII

Sérialise les objets en JSON encodé en ASCII. Si cette option est désactivée, le JSON renvoyé par jsonify contiendra des caractères Unicode. Cela a des implications en matière de sécurité lors du rendu du JSON en JavaScript dans les modèles, et devrait typiquement rester activé.

Valeur par défaut : True

JSON_SORT_KEYS

Trie les clés des objets JSON par ordre alphabétique. Cette fonction est utile pour la mise en cache car elle garantit que les données sont sérialisées de la même manière, quelle que soit la graine de hachage de Python. Bien que cela ne soit pas recommandé, vous pouvez désactiver cette fonction pour améliorer les performances au détriment de la mise en cache.

Valeur par défaut : True

JSONIFY_PRETTYPRINT_REGULAR

Les réponses jsonify seront affichées avec des nouvelles lignes, des espaces et des indentations pour une lecture plus facile par les humains. Toujours activé en mode débogage.

Valeur par défaut : False

JSONIFY_MIMETYPE

Le mimetype des réponses jsonify.

Valeur par défaut : application/json

TEMPLATES_AUTO_RELOAD

Recharge les modèles lorsqu’ils sont modifiés. Si elle n’est pas définie, elle sera activée en mode débogage.

Valeur par défaut : None

EXPLAIN_TEMPLATE_LOADING

Journalise les informations de débogage retraçant la façon dont un fichier modèle a été chargé. Cela peut être utile pour comprendre pourquoi un modèle n’a pas été chargé ou pourquoi le mauvais fichier semble avoir été chargé.

Valeur par défaut : False

Averti si les en-têtes de cookie sont plus grands que ce nombre d’octets. La valeur par défaut est 4093. Les cookies de grande taille peuvent être ignorés en silence par les navigateurs. Définissez cette valeur à « 0 » pour désactiver l’avertissement.

Changelog

Modifié dans la version 1.0: LOGGER_NAME et LOGGER_HANDLER_POLICY ont été supprimés. Voir Journalisation pour des informations sur la configuration.

Ajout de ENV pour refléter la variable d’environnement FLASK_ENV.

Ajout de SESSION_COOKIE_SAMESITE pour contrôler l’option SameSite du cookie de session.

Ajouté MAX_COOKIE_SIZE pour contrôler un avertissement de Werkzeug.

Nouveau dans la version 0.11: SESSION_REFRESH_EACH_REQUEST, TEMPLATES_AUTO_RELOAD, LOGGER_HANDLER_POLICY, EXPLAIN_TEMPLATE_LOADING

Nouveau dans la version 0.10: JSON_AS_ASCII, JSON_SORT_KEYS, JSONIFY_PRETTYPRINT_REGULAR

Nouveau dans la version 0.9: SCHÉMA D’URL PRÉFÉRÉE

Nouveau dans la version 0.8: TRAP_BAD_REQUEST_ERRORS, TRAP_HTTP_EXCEPTIONS, APPLICATION_ROOT, SESSION_COOKIE_DOMAIN, SESSION_COOKIE_PATH, SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SECURE

Nouveau dans la version 0.7: PROPAGATE_EXCEPTIONS, PRESERVE_CONTEXT_ON_EXCEPTION

Nouveau dans la version 0.6: MAX_CONTENT_LENGTH

Nouveau dans la version 0.5: SERVER_NAME

Nouveau dans la version 0.4: LOGGER_NAME

Configuration à partir de fichiers Python

La configuration devient plus utile si vous pouvez la stocker dans un fichier séparé, idéalement situé en dehors du paquet réel de l’application. Cela rend possible l’empaquetage et la distribution de votre application via divers outils de gestion de paquets (Deploying with Setuptools) et enfin la modification du fichier de configuration par la suite.

Un modèle commun est donc le suivant:

app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')

Il charge d’abord la configuration du module yourapplication.default_settings, puis remplace les valeurs par le contenu du fichier vers lequel pointe la variable d’environnement YOURAPPLICATION_SETTINGS. Cette variable d’environnement peut être définie dans le shell avant de démarrer le serveur :

$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ flask run
 * Running on http://127.0.0.1:5000/

Les fichiers de configuration eux-mêmes sont de véritables fichiers Python. Seules les valeurs en majuscules sont stockées dans l’objet config par la suite. Veillez donc à utiliser des lettres majuscules pour vos clés de configuration.

Voici un exemple de fichier de configuration:

# Example configuration
SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/'

Veillez à charger la configuration très tôt, afin que les extensions aient la possibilité d’y accéder au démarrage. Il existe d’autres méthodes sur l’objet config pour charger des fichiers individuels. Pour une référence complète, lisez la documentation de l’objet Config.

Configuration à partir de fichiers de données

Il est également possible de charger la configuration depuis un fichier dans le format de votre choix en utilisant from_file(). Par exemple, pour charger depuis un fichier TOML :

import toml
app.config.from_file("config.toml", load=toml.load)

Ou à partir d’un fichier JSON :

import json
app.config.from_file("config.json", load=json.load)

Configuration à partir de variables d’environnement

En plus de pointer vers des fichiers de configuration à l’aide de variables d’environnement, vous pouvez trouver utile (ou nécessaire) de contrôler vos valeurs de configuration directement depuis l’environnement.

Les variables d’environnement peuvent être définies dans le shell avant de démarrer le serveur :

$ export SECRET_KEY="5f352379324c22463451387a0aec5d2f"
$ export MAIL_ENABLED=false
$ flask run
 * Running on http://127.0.0.1:5000/

Bien que cette approche soit simple à utiliser, il est important de se rappeler que les variables d’environnement sont des chaînes de caractères – elles ne sont pas automatiquement désérialisées en types Python.

Voici un exemple de fichier de configuration qui utilise des variables d’environnement:

import os

_mail_enabled = os.environ.get("MAIL_ENABLED", default="true")
MAIL_ENABLED = _mail_enabled.lower() in {"1", "t", "true"}

SECRET_KEY = os.environ.get("SECRET_KEY")

if not SECRET_KEY:
    raise ValueError("No SECRET_KEY set for Flask application")

Notez que toute valeur autre qu’une chaîne vide sera interprétée comme une valeur booléenne True en Python, ce qui nécessite de faire attention si un environnement définit explicitement des valeurs destinées à être False.

Veillez à charger la configuration très tôt, afin que les extensions aient la possibilité d’y accéder au démarrage. Il existe d’autres méthodes sur l’objet config pour charger des fichiers individuels. Pour une référence complète, lisez la documentation de la classe Config.

Meilleures pratiques de configuration

L’inconvénient de l’approche mentionnée plus haut est qu’elle rend les tests un peu plus difficiles. Il n’y a pas de solution unique pour ce problème en général, mais il y a quelques éléments que vous pouvez garder à l’esprit pour améliorer cette expérience :

  1. Créez votre application dans une fonction et enregistrez des blueprints sur celle-ci. De cette façon, vous pouvez créer plusieurs instances de votre application avec différentes configurations attachées, ce qui facilite grandement les tests unitaires. Vous pouvez utiliser cette fonction pour transmettre la configuration si nécessaire.

  2. N’écrivez pas de code qui a besoin de la configuration au moment de l’importation. Si vous vous limitez à des accès sur demande à la configuration, vous pourrez reconfigurer l’objet ultérieurement si nécessaire.

Développement / Production

La plupart des applications nécessitent plus d’une configuration. Il doit y avoir au moins des configurations distinctes pour le serveur de production et celui utilisé pendant le développement. La manière la plus simple de gérer cela est d’utiliser une configuration par défaut qui est toujours chargée et fait partie du contrôle de version, et une configuration séparée qui remplace les valeurs si nécessaire comme mentionné dans l’exemple ci-dessus:

app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')

Il vous suffit alors d’ajouter un fichier séparé config.py et d’exporter YOURAPPLICATION_SETTINGS=/path/to/config.py et vous avez terminé. Cependant, il existe aussi d’autres moyens. Par exemple, vous pouvez utiliser les importations ou les sous-classes.

Ce qui est très populaire dans le monde de Django est de rendre l’importation explicite dans le fichier de configuration en ajoutant from yourapplication.default_settings import * en haut du fichier et ensuite de remplacer les changements à la main. Vous pouvez également inspecter une variable d’environnement comme YOURAPPLICATION_MODE et la définir sur production, développement, etc. et importer différents fichiers codés en dur en fonction de cela.

Un modèle intéressant est également d’utiliser les classes et l’héritage pour la configuration:

class Config(object):
    TESTING = False

class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(Config):
    DATABASE_URI = "sqlite:////tmp/foo.db"

class TestingConfig(Config):
    DATABASE_URI = 'sqlite:///:memory:'
    TESTING = True

Pour activer une telle configuration, il suffit de faire appel à from_object():

app.config.from_object('configmodule.ProductionConfig')

Notez que from_object() n’instancie pas l’objet classe. Si vous devez instancier la classe, par exemple pour accéder à une propriété, vous devez le faire avant d’appeler from_object():

from configmodule import ProductionConfig
app.config.from_object(ProductionConfig())

# Alternatively, import via string:
from werkzeug.utils import import_string
cfg = import_string('configmodule.ProductionConfig')()
app.config.from_object(cfg)

L’instanciation de l’objet de configuration vous permet d’utiliser @property dans vos classes de configuration:

class Config(object):
    """Base config, uses staging database server."""
    TESTING = False
    DB_SERVER = '192.168.1.56'

    @property
    def DATABASE_URI(self):  # Note: all caps
        return f"mysql://user@{self.DB_SERVER}/foo"

class ProductionConfig(Config):
    """Uses production database server."""
    DB_SERVER = '192.168.19.32'

class DevelopmentConfig(Config):
    DB_SERVER = 'localhost'

class TestingConfig(Config):
    DB_SERVER = 'localhost'
    DATABASE_URI = 'sqlite:///:memory:'

Il existe de nombreuses méthodes différentes et c’est à vous de décider comment vous voulez gérer vos fichiers de configuration. Voici cependant une liste de bonnes recommandations :

  • Conservez une configuration par défaut dans le contrôle de version. Vous pouvez soit remplir la configuration avec cette configuration par défaut, soit l’importer dans vos propres fichiers de configuration avant de remplacer les valeurs.

  • Utilisez une variable d’environnement pour passer d’une configuration à l’autre. Cela peut se faire depuis l’extérieur de l’interpréteur Python et facilite grandement le développement et le déploiement car vous pouvez rapidement et facilement passer d’une configuration à l’autre sans avoir à toucher au code. Si vous travaillez souvent sur différents projets, vous pouvez même créer votre propre script pour le sourcing qui active un virtualenv et exporte la configuration de développement pour vous.

  • Utilisez un outil comme fabric en production pour pousser le code et les configurations séparément sur le(s) serveur(s) de production. Pour plus de détails sur la façon de procéder, consultez le modèle Deploying with Fabric.

Dossiers d’instance

Changelog

Nouveau dans la version 0.8.

Flask 0.8 introduit les dossiers d’instance. Pendant longtemps, Flask permettait de se référer directement aux chemins relatifs au dossier de l’application (via Flask.root_path). C’était aussi la façon dont de nombreux développeurs chargeaient les configurations stockées à côté de l’application. Malheureusement, cela ne fonctionne bien que si les applications ne sont pas des paquets, auquel cas le chemin racine fait référence au contenu du paquet.

Avec Flask 0.8, un nouvel attribut a été introduit:Flask.instance_path. Il fait référence à un nouveau concept appelé « dossier d’instance ». Le dossier d’instance est conçu pour ne pas être sous contrôle de version et être spécifique au déploiement. C’est l’endroit parfait pour déposer des choses qui changent au moment de l’exécution ou des fichiers de configuration.

Vous pouvez soit fournir explicitement le chemin du dossier de l’instance lors de la création de l’application Flask ou vous pouvez laisser Flask détecter automatiquement le dossier de l’instance. Pour une configuration explicite, utilisez le paramètre instance_path:

app = Flask(__name__, instance_path='/path/to/instance/folder')

Gardez à l’esprit que ce chemin doit être absolu lorsqu’il est fourni.

Si le paramètre instance_path n’est pas fourni, les emplacements par défaut suivants sont utilisés :

  • Module désinstallé:

    /myapp.py
    /instance
    
  • Paquet désinstallé:

    /myapp
        /__init__.py
    /instance
    
  • Module ou paquet installé:

    $PREFIX/lib/pythonX.Y/site-packages/myapp
    $PREFIX/var/myapp-instance
    

    $PREFIX est le préfixe de votre installation Python. Cela peut être /usr ou le chemin vers votre virtualenv. Vous pouvez afficher la valeur de sys.prefix pour voir à quoi correspond le préfixe.

Puisque l’objet config permet le chargement de fichiers de configuration à partir de noms de fichiers relatifs, nous avons fait en sorte qu’il soit possible de changer le chargement via les noms de fichiers pour qu’il soit relatif au chemin de l’instance si on le souhaite. Le comportement des chemins relatifs dans les fichiers de configuration peut être basculé entre « relatif à la racine de l’application » (par défaut) et « relatif au dossier de l’instance » via le commutateur instance_relative_config du constructeur de l’application:

app = Flask(__name__, instance_relative_config=True)

Voici un exemple complet de la façon de configurer Flask pour précharger la configuration d’un module et ensuite remplacer la configuration d’un fichier dans le dossier de l’instance s’il existe:

app = Flask(__name__, instance_relative_config=True)
app.config.from_object('yourapplication.default_settings')
app.config.from_pyfile('application.cfg', silent=True)

Le chemin d’accès au dossier de l’instance peut être trouvé via l’attribut Flask.instance_path. Flask fournit également un raccourci pour ouvrir un fichier depuis le dossier d’instance avec Flask.open_instance_resource().

Exemple d’utilisation pour les deux:

filename = os.path.join(app.instance_path, 'application.cfg')
with open(filename) as f:
    config = f.read()

# or via open_instance_resource:
with app.open_instance_resource('application.cfg') as f:
    config = f.read()