Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ __pycache__
htmlcov/
.cache/
.idea
.pytest_cache/
182 changes: 99 additions & 83 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,38 @@ production using `12-factor <http://12factor.net/>`__ principles.

Usages
======
The easiest and most common usage consists on calling ``load_dotenv`` when the
application starts, which will load environment variables from a file named
``.env`` in the current directory or any of its parents or from the path specificied;
after that, you can just call the environment-related method you need as
provided by ``os.getenv``.

``.env`` looks like this:

.. code:: shell

# a comment and that will be ignored.
REDIS_ADDRESS=localhost:6379
MEANING_OF_LIFE=42
MULTILINE_VAR="hello\nworld"

You can optionally prefix each line with the word ``export``, which will
conveniently allow you to source the whole file on your shell.

``.env`` can interpolate variables using POSIX variable expansion, variables
are replaced from the environment first or from other values in the ``.env``
file if the variable is not present in the environment. (``Note``: Default Value
Expansion is not supported as of yet, see `#30 <https://github.com/theskumar/python-dotenv/pull/30#issuecomment-244036604>`__.)

.. code:: shell

CONFIG_PATH=${HOME}/.config/foo
DOMAIN=example.org
EMAIL=admin@${DOMAIN}


Getting started
================

Assuming you have created the ``.env`` file along-side your settings
module.
Expand All @@ -43,66 +75,64 @@ Add the following code to your ``settings.py``
.. code:: python

# settings.py
from os.path import join, dirname
from dotenv import load_dotenv

dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)
load_dotenv()

# OR, the same with increased verbosity:
load_dotenv(dotenv_path, verbose=True)

Alternatively, you can use ``find_dotenv()`` method that will try to find a
``.env`` file by (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 (b) walking up the directory tree looking for the
specified file -- called ``.env`` by default.
load_dotenv(verbose=True)

.. code:: python
# OR, explicitly providing path to '.env'
from pathlib import Path # python3 only
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)

from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())

You can also set ``load_dotenv`` to override existing variables:
At this point, parsed key/value from the `.env` file is now present as system
environment variable and they can be conveniently accessed via ``os.getenv()``

.. code:: python

from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv(), override=True)
# settings.py
import os
SECRET_KEY = os.getenv("EMAIL")
DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD")


Now, you can access the variables either from system environment
variable or loaded from ``.env`` file. **System environment variables
gets higher precedence** and it's advised not to include it in version control.
``load_dotenv`` do not override existing System environment variables, but
you can do so if you want by passing ``override=True`` to ``load_dotenv()``.

You can use ``find_dotenv()`` method that will try to find a ``.env`` file by
(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
(b) walking up the directory tree looking for the specified file -- called ``.env`` by default.

.. code:: python

# settings.py
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())

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

In-memory filelikes
-------------------

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

.. code:: shell
.. code:: python

SOME_VAR=someval
# I am a comment and that is OK
FOO="BAR"
MULTILINE_VAR="hello\nworld"
>>> from io import StringIO # Python2: from StringIO import StringIO
>>> from dotenv import dotenv_values
>>> filelike = StringIO('SPAM=EGSS\n')
>>> filelike.seek(0)
>>> parsed = dotenv_values(stream=filelike)
>>> parsed['SPAM']
'EGSS'

``.env`` can interpolate variables using POSIX variable expansion, variables
are replaced from the environment first or from other values in the ``.env``
file if the variable is not present in the environment. (``Note``: Default Value
Expansion is not supported as of yet, see `#30 <https://github.com/theskumar/python-dotenv/pull/30#issuecomment-244036604>`__.)

.. code:: shell
The returned value is dictionary with key value pair.

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


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


In-memory filelikes
-------------------
Installation
============

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

.. code:: python
pip install -U python-dotenv

from io import StringIO # Python2: from StringIO import StringIO
from dotenv.main import parse_dotenv
filelike = StringIO('SPAM=EGSS\n')
filelike.seek(0)
parsed = parse_dotenv(stream=filelike)

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

.. code:: python
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.

os_env_like = dict(iter(parsed))
assert os_env_like['SPAM'] == 'EGGS'
::

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

# Use find_dotenv to locate the file
%dotenv

Installation
============
# Specify a particular file
%dotenv relative/or/absolute/path/to/.env

# Use _-o_ to indicate override of existing variables
%dotenv -o

# Use _-v_ to turn verbose mode on
%dotenv -v

::

pip install -U python-dotenv

Command-line interface
======================
=================

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

iPython Support
---------------

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.

::

%load_ext dotenv

# Use find_dotenv to locate the file
%dotenv

# Specify a particular file
%dotenv relative/or/absolute/path/to/.env

# Use _-o_ to indicate override of existing variables
%dotenv -o

# Use _-v_ to turn verbose mode on
%dotenv -v


Setting config on remote servers
--------------------------------
Expand Down Expand Up @@ -235,6 +241,7 @@ Get all your remote config info with ``fab config``
::

$ fab config
foo="bar"

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

Expand Down Expand Up @@ -292,6 +299,13 @@ Executing the tests:
Changelog
=========

0.8.0
----------------------------
- ``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.
- Add support for ``export `` prefix in the line.
- Internal refractoring (`@theskumar`_)
- Allow ``load_dotenv`` and ``dotenv_values`` to work with ``StringIO())`` (`@alanjds`_)(`@theskumar`_) (`#78`_)

0.7.1
----------------------------

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

.. _@alanjds: https://github.com/alanjds
.. _@milonimrod: https://github.com/milonimrod
.. _@maxkoryukov: https://github.com/maxkoryukov
.. _@pjona: https://github.com/pjona
Expand All @@ -352,6 +367,7 @@ Changelog
.. _@paulochf: https://github.com/paulochf
.. _@theskumar: https://github.com/theskumar

.. _#78: https://github.com/theskumar/python-dotenv/issues/78
.. _#63: https://github.com/theskumar/python-dotenv/issues/63
.. _#60: https://github.com/theskumar/python-dotenv/issues/60
.. _#57: https://github.com/theskumar/python-dotenv/issues/57
Expand Down
11 changes: 9 additions & 2 deletions dotenv/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from .cli import get_cli_string
from .main import load_dotenv, get_key, set_key, unset_key, find_dotenv
from .main import load_dotenv, get_key, set_key, unset_key, find_dotenv, dotenv_values


__all__ = ['get_cli_string', 'load_dotenv', 'get_key', 'set_key', 'unset_key', 'find_dotenv', 'load_ipython_extension']
__all__ = ['get_cli_string',
'load_dotenv',
'dotenv_values',
'get_key',
'set_key',
'unset_key',
'find_dotenv',
'load_ipython_extension']


def load_ipython_extension(ipython):
Expand Down
2 changes: 1 addition & 1 deletion dotenv/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import click

from .main import get_key, dotenv_values, set_key, unset_key
from .main import dotenv_values, get_key, set_key, unset_key


@click.group()
Expand Down
4 changes: 4 additions & 0 deletions dotenv/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
try:
from StringIO import StringIO # noqa
except ImportError:
from io import StringIO # noqa
5 changes: 3 additions & 2 deletions dotenv/ipython.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import print_function
from .main import load_dotenv, find_dotenv

from IPython.core.magic import Magics, magics_class, line_magic
from IPython.core.magic import Magics, line_magic, magics_class
from IPython.core.magic_arguments import (argument, magic_arguments,
parse_argstring)

from .main import find_dotenv, load_dotenv


@magics_class
class IPythonDotEnv(Magics):
Expand Down
Loading