Skip to content

Commit 9f1ffc3

Browse files
authored
Allow load_dotenv and dotenv_values to work with StringIO objects (theskumar#98)
* Allow `load_dotenv` and `dotenv_values` to work with StringIO objects * make flake8 happy * Fix for python 2.7 * Try fixing unicode issue in python2 * python 2.7 related fixes * Update docs * Doc update
1 parent 84d16c4 commit 9f1ffc3

File tree

9 files changed

+272
-182
lines changed

9 files changed

+272
-182
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ __pycache__
99
htmlcov/
1010
.cache/
1111
.idea
12+
.pytest_cache/

README.rst

Lines changed: 99 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,38 @@ production using `12-factor <http://12factor.net/>`__ principles.
2828

2929
Usages
3030
======
31+
The easiest and most common usage consists on calling ``load_dotenv`` when the
32+
application starts, which will load environment variables from a file named
33+
``.env`` in the current directory or any of its parents or from the path specificied;
34+
after that, you can just call the environment-related method you need as
35+
provided by ``os.getenv``.
36+
37+
``.env`` looks like this:
38+
39+
.. code:: shell
40+
41+
# a comment and that will be ignored.
42+
REDIS_ADDRESS=localhost:6379
43+
MEANING_OF_LIFE=42
44+
MULTILINE_VAR="hello\nworld"
45+
46+
You can optionally prefix each line with the word ``export``, which will
47+
conveniently allow you to source the whole file on your shell.
48+
49+
``.env`` can interpolate variables using POSIX variable expansion, variables
50+
are replaced from the environment first or from other values in the ``.env``
51+
file if the variable is not present in the environment. (``Note``: Default Value
52+
Expansion is not supported as of yet, see `#30 <https://github.com/theskumar/python-dotenv/pull/30#issuecomment-244036604>`__.)
53+
54+
.. code:: shell
55+
56+
CONFIG_PATH=${HOME}/.config/foo
57+
DOMAIN=example.org
58+
EMAIL=admin@${DOMAIN}
59+
60+
61+
Getting started
62+
================
3163

3264
Assuming you have created the ``.env`` file along-side your settings
3365
module.
@@ -43,66 +75,64 @@ Add the following code to your ``settings.py``
4375
.. code:: python
4476
4577
# settings.py
46-
from os.path import join, dirname
4778
from dotenv import load_dotenv
48-
49-
dotenv_path = join(dirname(__file__), '.env')
50-
load_dotenv(dotenv_path)
79+
load_dotenv()
5180
5281
# OR, the same with increased verbosity:
53-
load_dotenv(dotenv_path, verbose=True)
54-
55-
Alternatively, you can use ``find_dotenv()`` method that will try to find a
56-
``.env`` file by (a) guessing where to start using ``__file__`` or the working
57-
directory -- allowing this to work in non-file contexts such as IPython notebooks
58-
and the REPL, and then (b) walking up the directory tree looking for the
59-
specified file -- called ``.env`` by default.
82+
load_dotenv(verbose=True)
6083
61-
.. code:: python
84+
# OR, explicitly providing path to '.env'
85+
from pathlib import Path # python3 only
86+
env_path = Path('.') / '.env'
87+
load_dotenv(dotenv_path=env_path)
6288
63-
from dotenv import load_dotenv, find_dotenv
64-
load_dotenv(find_dotenv())
6589
66-
You can also set ``load_dotenv`` to override existing variables:
90+
At this point, parsed key/value from the `.env` file is now present as system
91+
environment variable and they can be conveniently accessed via ``os.getenv()``
6792

6893
.. code:: python
6994
70-
from dotenv import load_dotenv, find_dotenv
71-
load_dotenv(find_dotenv(), override=True)
95+
# settings.py
96+
import os
97+
SECRET_KEY = os.getenv("EMAIL")
98+
DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD")
99+
72100
73-
Now, you can access the variables either from system environment
74-
variable or loaded from ``.env`` file. **System environment variables
75-
gets higher precedence** and it's advised not to include it in version control.
101+
``load_dotenv`` do not override existing System environment variables, but
102+
you can do so if you want by passing ``override=True`` to ``load_dotenv()``.
103+
104+
You can use ``find_dotenv()`` method that will try to find a ``.env`` file by
105+
(a) guessing where to start using ``__file__`` or the working directory -- allowing this to work in non-file contexts such as IPython notebooks and the REPL, and then
106+
(b) walking up the directory tree looking for the specified file -- called ``.env`` by default.
76107

77108
.. code:: python
78109
79-
# settings.py
110+
from dotenv import load_dotenv, find_dotenv
111+
load_dotenv(find_dotenv())
80112
81-
SECRET_KEY = os.environ.get("SECRET_KEY")
82-
DATABASE_PASSWORD = os.environ.get("DATABASE_PASSWORD")
83113
114+
In-memory filelikes
115+
-------------------
84116

85-
``.env`` is a simple text file. With each environment variables listed
86-
per line, in the format of ``KEY="Value"``, lines starting with `#` is
87-
ignored.
117+
It is possible to not rely on the filesystem to parse filelikes from other sources
118+
(e.g. from a network storage). ``load_dotenv`` and ``dotenv_values`` accepts a filelike ``stream``.
119+
Just be sure to rewind it before passing.
88120

89-
.. code:: shell
121+
.. code:: python
90122
91-
SOME_VAR=someval
92-
# I am a comment and that is OK
93-
FOO="BAR"
94-
MULTILINE_VAR="hello\nworld"
123+
>>> from io import StringIO # Python2: from StringIO import StringIO
124+
>>> from dotenv import dotenv_values
125+
>>> filelike = StringIO('SPAM=EGSS\n')
126+
>>> filelike.seek(0)
127+
>>> parsed = dotenv_values(stream=filelike)
128+
>>> parsed['SPAM']
129+
'EGSS'
95130
96-
``.env`` can interpolate variables using POSIX variable expansion, variables
97-
are replaced from the environment first or from other values in the ``.env``
98-
file if the variable is not present in the environment. (``Note``: Default Value
99-
Expansion is not supported as of yet, see `#30 <https://github.com/theskumar/python-dotenv/pull/30#issuecomment-244036604>`__.)
100131
101-
.. code:: shell
132+
The returned value is dictionary with key value pair.
102133

103-
CONFIG_PATH=${HOME}/.config/foo
104-
DOMAIN=example.org
105-
EMAIL=admin@${DOMAIN}
134+
``dotenv_values`` could be useful if you need to *consume* the envfile but not *apply* it
135+
directly into the system environment.
106136

107137

108138
Django
@@ -112,42 +142,39 @@ If you are using django you should add the above loader script at the
112142
top of ``wsgi.py`` and ``manage.py``.
113143

114144

115-
In-memory filelikes
116-
-------------------
145+
Installation
146+
============
117147

118-
Is possible to not rely on the filesystem to parse filelikes from other sources
119-
(e.g. from a network storage). ``parse_dotenv`` accepts a filelike `stream`.
120-
Just be sure to rewind it before passing.
148+
::
121149

122-
.. code:: python
150+
pip install -U python-dotenv
123151

124-
from io import StringIO # Python2: from StringIO import StringIO
125-
from dotenv.main import parse_dotenv
126-
filelike = StringIO('SPAM=EGSS\n')
127-
filelike.seek(0)
128-
parsed = parse_dotenv(stream=filelike)
129152

130-
The returned lazy generator yields ``(key,value)`` tuples.
131-
To ease the consumption, is suggested to unpack it into a dictionary.
153+
iPython Support
154+
---------------
132155

133-
.. code:: python
156+
You can use dotenv with iPython. You can either let the dotenv search for .env with `%dotenv` or provide the path to .env file explicitly, see below for usages.
134157

135-
os_env_like = dict(iter(parsed))
136-
assert os_env_like['SPAM'] == 'EGGS'
158+
::
137159

138-
This could be useful if you need to *consume* the envfile but not *apply* it
139-
directly into the system environment.
160+
%load_ext dotenv
140161

162+
# Use find_dotenv to locate the file
163+
%dotenv
141164

142-
Installation
143-
============
165+
# Specify a particular file
166+
%dotenv relative/or/absolute/path/to/.env
167+
168+
# Use _-o_ to indicate override of existing variables
169+
%dotenv -o
170+
171+
# Use _-v_ to turn verbose mode on
172+
%dotenv -v
144173

145-
::
146174

147-
pip install -U python-dotenv
148175

149176
Command-line interface
150-
======================
177+
=================
151178

152179
A cli interface ``dotenv`` is also included, which helps you manipulate
153180
the ``.env`` file without manually opening it. The same cli installed on
@@ -175,27 +202,6 @@ update your settings on remote server, handy isn't it!
175202
set Store the given key/value.
176203
unset Removes the given key.
177204

178-
iPython Support
179-
---------------
180-
181-
You can use dotenv with iPython. You can either let the dotenv search for .env with `%dotenv` or provide the path to .env file explicitly, see below for usages.
182-
183-
::
184-
185-
%load_ext dotenv
186-
187-
# Use find_dotenv to locate the file
188-
%dotenv
189-
190-
# Specify a particular file
191-
%dotenv relative/or/absolute/path/to/.env
192-
193-
# Use _-o_ to indicate override of existing variables
194-
%dotenv -o
195-
196-
# Use _-v_ to turn verbose mode on
197-
%dotenv -v
198-
199205

200206
Setting config on remote servers
201207
--------------------------------
@@ -235,6 +241,7 @@ Get all your remote config info with ``fab config``
235241
::
236242

237243
$ fab config
244+
foo="bar"
238245

239246
Set remote config variables with ``fab config:set,<key>,<value>``
240247

@@ -292,6 +299,13 @@ Executing the tests:
292299
Changelog
293300
=========
294301

302+
0.8.0
303+
----------------------------
304+
- ``set_key`` and ``unset_key`` only modified the affected file instead of parsing and re-writing file, this causes comments and other file entact as it is.
305+
- Add support for ``export `` prefix in the line.
306+
- Internal refractoring (`@theskumar`_)
307+
- Allow ``load_dotenv`` and ``dotenv_values`` to work with ``StringIO())`` (`@alanjds`_)(`@theskumar`_) (`#78`_)
308+
295309
0.7.1
296310
----------------------------
297311

@@ -340,6 +354,7 @@ Changelog
340354
- cli: Added ``-q/--quote`` option to control the behaviour of quotes around values in ``.env``. (Thanks `@hugochinchilla`_).
341355
- Improved test coverage.
342356

357+
.. _@alanjds: https://github.com/alanjds
343358
.. _@milonimrod: https://github.com/milonimrod
344359
.. _@maxkoryukov: https://github.com/maxkoryukov
345360
.. _@pjona: https://github.com/pjona
@@ -352,6 +367,7 @@ Changelog
352367
.. _@paulochf: https://github.com/paulochf
353368
.. _@theskumar: https://github.com/theskumar
354369

370+
.. _#78: https://github.com/theskumar/python-dotenv/issues/78
355371
.. _#63: https://github.com/theskumar/python-dotenv/issues/63
356372
.. _#60: https://github.com/theskumar/python-dotenv/issues/60
357373
.. _#57: https://github.com/theskumar/python-dotenv/issues/57

dotenv/__init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
from .cli import get_cli_string
2-
from .main import load_dotenv, get_key, set_key, unset_key, find_dotenv
2+
from .main import load_dotenv, get_key, set_key, unset_key, find_dotenv, dotenv_values
33

44

5-
__all__ = ['get_cli_string', 'load_dotenv', 'get_key', 'set_key', 'unset_key', 'find_dotenv', 'load_ipython_extension']
5+
__all__ = ['get_cli_string',
6+
'load_dotenv',
7+
'dotenv_values',
8+
'get_key',
9+
'set_key',
10+
'unset_key',
11+
'find_dotenv',
12+
'load_ipython_extension']
613

714

815
def load_ipython_extension(ipython):

dotenv/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import click
44

5-
from .main import get_key, dotenv_values, set_key, unset_key
5+
from .main import dotenv_values, get_key, set_key, unset_key
66

77

88
@click.group()

dotenv/compat.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
try:
2+
from StringIO import StringIO # noqa
3+
except ImportError:
4+
from io import StringIO # noqa

dotenv/ipython.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from __future__ import print_function
2-
from .main import load_dotenv, find_dotenv
32

4-
from IPython.core.magic import Magics, magics_class, line_magic
3+
from IPython.core.magic import Magics, line_magic, magics_class
54
from IPython.core.magic_arguments import (argument, magic_arguments,
65
parse_argstring)
76

7+
from .main import find_dotenv, load_dotenv
8+
89

910
@magics_class
1011
class IPythonDotEnv(Magics):

0 commit comments

Comments
 (0)