Giter VIP home page Giter VIP logo

python-decouple's Introduction

Python Decouple: Strict separation of settings from code

Decouple helps you to organize your settings so that you can change parameters without having to redeploy your app.

It also makes it easy for you to:

  1. store parameters in ini or .env files;
  2. define comprehensive default values;
  3. properly convert values to the correct data type;
  4. have only one configuration module to rule all your instances.

It was originally designed for Django, but became an independent generic tool for separating settings from code.

Build Status Latest PyPI version

The settings files in web frameworks store many different kinds of parameters:

  • Locale and i18n;
  • Middlewares and Installed Apps;
  • Resource handles to the database, Memcached, and other backing services;
  • Credentials to external services such as Amazon S3 or Twitter;
  • Per-deploy values such as the canonical hostname for the instance.

The first 2 are project settings and the last 3 are instance settings.

You should be able to change instance settings without redeploying your app.

Envvars works, but since os.environ only returns strings, it's tricky.

Let's say you have an envvar DEBUG=False. If you run:

if os.environ['DEBUG']:
    print True
else:
    print False

It will print True, because os.environ['DEBUG'] returns the string "False". Since it's a non-empty string, it will be evaluated as True.

Decouple provides a solution that doesn't look like a workaround: config('DEBUG', cast=bool).

Install:

pip install python-decouple

Then use it on your settings.py.

  1. Import the config object:

    from decouple import config
  2. Retrieve the configuration parameters:

    SECRET_KEY = config('SECRET_KEY')
    DEBUG = config('DEBUG', default=False, cast=bool)
    EMAIL_HOST = config('EMAIL_HOST', default='localhost')
    EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)

Decouple's default encoding is UTF-8.

But you can specify your preferred encoding.

Since config is lazy and only opens the configuration file when it's first needed, you have the chance to change its encoding right after import.

from decouple import config
config.encoding = 'cp1251'
SECRET_KEY = config('SECRET_KEY')

If you wish to fall back to your system's default encoding use:

import locale
from decouple import config
config.encoding = locale.getpreferredencoding(False)
SECRET_KEY = config('SECRET_KEY')

Decouple supports both .ini and .env files.

Simply create a settings.ini next to your configuration module in the form:

[settings]
DEBUG=True
TEMPLATE_DEBUG=%(DEBUG)s
SECRET_KEY=ARANDOMSECRETKEY
DATABASE_URL=mysql://myuser:mypassword@myhost/mydatabase
PERCENTILE=90%%
#COMMENTED=42

Note: Since ConfigParser supports string interpolation, to represent the character % you need to escape it as %%.

Simply create a .env text file in your repository's root directory in the form:

DEBUG=True
TEMPLATE_DEBUG=True
SECRET_KEY=ARANDOMSECRETKEY
DATABASE_URL=mysql://myuser:mypassword@myhost/mydatabase
PERCENTILE=90%
#COMMENTED=42

Given that I have a .env file in my repository's root directory, here is a snippet of my settings.py.

I also recommend using pathlib and dj-database-url.

# coding: utf-8
from decouple import config
from unipath import Path
from dj_database_url import parse as db_url


BASE_DIR = Path(__file__).parent

DEBUG = config('DEBUG', default=False, cast=bool)
TEMPLATE_DEBUG = DEBUG

DATABASES = {
    'default': config(
        'DATABASE_URL',
        default='sqlite:///' + BASE_DIR.child('db.sqlite3'),
        cast=db_url
    )
}

TIME_ZONE = 'America/Sao_Paulo'
USE_L10N = True
USE_TZ = True

SECRET_KEY = config('SECRET_KEY')

EMAIL_HOST = config('EMAIL_HOST', default='localhost')
EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='')
EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='')
EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=False, cast=bool)

# ...

In the above example, all configuration parameters except SECRET_KEY = config('SECRET_KEY') have a default value in case it does not exist in the .env file.

If SECRET_KEY is not present in the .env, decouple will raise an UndefinedValueError.

This fail fast policy helps you avoid chasing misbehaviours when you eventually forget a parameter.

Sometimes you may want to change a parameter value without having to edit the .ini or .env files.

Since version 3.0, decouple respects the unix way. Therefore environment variables have precedence over config files.

To override a config parameter you can simply do:

DEBUG=True python manage.py

Decouple always searches for Options in this order:

  1. Environment variables;
  2. Repository: ini or .env file;
  3. Default argument passed to config.

There are 4 classes doing the magic:

  • Config

    Coordinates all the configuration retrieval.

  • RepositoryIni

    Can read values from os.environ and ini files, in that order.

    Note: Since version 3.0 decouple respects unix precedence of environment variables over config files.

  • RepositoryEnv

    Can read values from os.environ and .env files.

    Note: Since version 3.0 decouple respects unix precedence of environment variables over config files.

  • AutoConfig

    This is a lazy Config factory that detects which configuration repository you're using.

    It recursively searches up your configuration module path looking for a settings.ini or a .env file.

    Optionally, it accepts search_path argument to explicitly define where the search starts.

The config object is an instance of AutoConfig that instantiates a Config with the proper Repository on the first time it is used.

By default, all values returned by decouple are strings, after all they are read from text files or the envvars.

However, your Python code may expect some other value type, for example:

  • Django's DEBUG expects a boolean True or False.
  • Django's EMAIL_PORT expects an integer.
  • Django's ALLOWED_HOSTS expects a list of hostnames.
  • Django's SECURE_PROXY_SSL_HEADER expects a tuple with two elements, the name of the header to look for and the required value.

To meet this need, the config function accepts a cast argument which receives any callable, that will be used to transform the string value into something else.

Let's see some examples for the above mentioned cases:

>>> os.environ['DEBUG'] = 'False'
>>> config('DEBUG', cast=bool)
False

>>> os.environ['EMAIL_PORT'] = '42'
>>> config('EMAIL_PORT', cast=int)
42

>>> os.environ['ALLOWED_HOSTS'] = '.localhost, .herokuapp.com'
>>> config('ALLOWED_HOSTS', cast=lambda v: [s.strip() for s in v.split(',')])
['.localhost', '.herokuapp.com']

>>> os.environ['SECURE_PROXY_SSL_HEADER'] = 'HTTP_X_FORWARDED_PROTO, https'
>>> config('SECURE_PROXY_SSL_HEADER', cast=Csv(post_process=tuple))
('HTTP_X_FORWARDED_PROTO', 'https')

As you can see, cast is very flexible. But the last example got a bit complex.

To address the complexity of the last example, Decouple comes with an extensible Csv helper.

Let's improve the last example:

>>> from decouple import Csv
>>> os.environ['ALLOWED_HOSTS'] = '.localhost, .herokuapp.com'
>>> config('ALLOWED_HOSTS', cast=Csv())
['.localhost', '.herokuapp.com']

You can also have a default value that must be a string to be processed by Csv.

>>> from decouple import Csv
>>> config('ALLOWED_HOSTS', default='127.0.0.1', cast=Csv())
['127.0.0.1']

You can also parametrize the Csv Helper to return other types of data.

>>> os.environ['LIST_OF_INTEGERS'] = '1,2,3,4,5'
>>> config('LIST_OF_INTEGERS', cast=Csv(int))
[1, 2, 3, 4, 5]

>>> os.environ['COMPLEX_STRING'] = '%virtual_env%\t *important stuff*\t   trailing spaces   '
>>> csv = Csv(cast=lambda s: s.upper(), delimiter='\t', strip=' %*')
>>> csv(os.environ['COMPLEX_STRING'])
['VIRTUAL_ENV', 'IMPORTANT STUFF', 'TRAILING SPACES']

By default Csv returns a list, but you can get a tuple or whatever you want using the post_process argument:

>>> os.environ['SECURE_PROXY_SSL_HEADER'] = 'HTTP_X_FORWARDED_PROTO, https'
>>> config('SECURE_PROXY_SSL_HEADER', cast=Csv(post_process=tuple))
('HTTP_X_FORWARDED_PROTO', 'https')

Allows for cast and validation based on a list of choices. For example:

>>> from decouple import config, Choices
>>> os.environ['CONNECTION_TYPE'] = 'usb'
>>> config('CONNECTION_TYPE', cast=Choices(['eth', 'usb', 'bluetooth']))
'usb'

>>> os.environ['CONNECTION_TYPE'] = 'serial'
>>> config('CONNECTION_TYPE', cast=Choices(['eth', 'usb', 'bluetooth']))
Traceback (most recent call last):
 ...
ValueError: Value not in list: 'serial'; valid values are ['eth', 'usb', 'bluetooth']

You can also parametrize Choices helper to cast to another type:

>>> os.environ['SOME_NUMBER'] = '42'
>>> config('SOME_NUMBER', cast=Choices([7, 14, 42], cast=int))
42

You can also use a Django-like choices tuple:

>>> USB = 'usb'
>>> ETH = 'eth'
>>> BLUETOOTH = 'bluetooth'
>>>
>>> CONNECTION_OPTIONS = (
...        (USB, 'USB'),
...        (ETH, 'Ethernet'),
...        (BLUETOOTH, 'Bluetooth'),)
...
>>> os.environ['CONNECTION_TYPE'] = BLUETOOTH
>>> config('CONNECTION_TYPE', cast=Choices(choices=CONNECTION_OPTIONS))
'bluetooth'
import os
from decouple import Config, RepositoryEnv


config = Config(RepositoryEnv("path/to/.env"))
import os
from decouple import Config, RepositoryEnv


config = Config(RepositoryEnv("path/to/.env"))
import os
from decouple import Config, RepositoryEnv


config = Config(RepositoryEnv("path/to/somefile-like-env"))
import os
from decouple import Config, RepositoryEnv


DOTENV_FILE = os.environ.get("DOTENV_FILE", ".env") # only place using os.environ
config = Config(RepositoryEnv(DOTENV_FILE))
from collections import ChainMap
from decouple import Config, RepositoryEnv


config = Config(ChainMap(RepositoryEnv(".private.env"), RepositoryEnv(".env")))

Your contribution is welcome.

Setup your development environment:

git clone [email protected]:henriquebastos/python-decouple.git
cd python-decouple
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
tox

Decouple supports both Python 2.7 and 3.6. Make sure you have both installed.

I use pyenv to manage multiple Python versions and I described my workspace setup on this article: The definitive guide to setup my Python workspace

You can submit pull requests and issues for discussion. However I only consider merging tested code.

The MIT License (MIT)

Copyright (c) 2017 Henrique Bastos <henrique at bastos dot net>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

python-decouple's People

Contributors

0xc4n1 avatar 3vilwind avatar b0o avatar blueyed avatar danielgoncalves avatar flavioamieiro avatar henriquebastos avatar ibachar-es avatar iurisilvio avatar jayennis22 avatar jonafato avatar kianmeng avatar kivs avatar kojiromike avatar lpmi-13 avatar lucaspolo avatar lucasrcezimbra avatar luzfcb avatar mehrh8 avatar mjr avatar osantana avatar pietrodn avatar rmax avatar stevejalim avatar thiagogds avatar timgates42 avatar tirkarthi avatar willkg avatar xmnlab avatar zandorsabino avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-decouple's Issues

Add yml support.

The yml support is largely used in several libs, like ansible.

The format allows very clear configuration and improve the readability.

Thanks for your work, very useful lib.

Show ini or env being used when raising UndefinedValueError

When decouple can't find the settings.ini it raises an error sucha as this:

/usr/local/lib/python3.5/dist-packages/decouple.py in __call__(self, *args, **kwargs)
    195             self._load(self._caller_path())
    196 
--> 197         return self.config(*args, **kwargs)
    198 
    199 

/usr/local/lib/python3.5/dist-packages/decouple.py in __call__(self, *args, **kwargs)
     75         Convenient shortcut to get.
     76         """
---> 77         return self.get(*args, **kwargs)
     78 
     79 

/usr/local/lib/python3.5/dist-packages/decouple.py in get(self, option, default, cast)
     62 
     63         if isinstance(value, Undefined):
---> 64             raise UndefinedValueError('%s option not found and default value was not defined.' % option)
     65 
     66         if isinstance(cast, Undefined):

UndefinedValueError: PSQL_USER option not found and default value was not defined.

It would be nice if this Error message gave the user more information to fix the problem, such as where decouple has searched and not found the missing option.

Problem with Openshift Platform and .env folder

The Openshift Platform uses a ".env" folder, that has VARIABLE_NAME file with the value as file content, and that conflicts with decouple.

Maybe we need to create other Parser for this format, or change the .env parser.

Traceback:
File "/var/lib/openshift/558098a099fc7700aa/app-root/runtime/dependencies/python/virtenv/lib/python2.7/site-packages/decouple.py", line 196, in call
self._load(self._caller_path())
File "/var/lib/openshift/558098a099fc7700aa/app-root/runtime/dependencies/python/virtenv/lib/python2.7/site-packages/decouple.py", line 186, in _load
self.config = Config(Repository(filename))
File "/var/lib/openshift/558098a099fc7700aa/app-root/runtime/dependencies/python/virtenv/lib/python2.7/site-packages/decouple.py", line 115, in init
for line in open(source):
IOError: [Errno 21] Is a directory: '/var/lib/openshift/558098a099fc7700aa/.env'

Feature Request / Console input

It would be cool if there were a way to ask the console for config input if its not set in environment or settings file.

This functionality should probably only be enabled if it detects a tty console, or maybe an INTERACTIVE=1 env var....

Anyway, this is pretty minor. If I have some free time in the near future, I'll do a PR

More documentation/examples using RepositoryEnv, RepositoryIni

If the documentation had spelled out that AutoConfig is the default and examples of RepositoryEnv/Ini that would have been helpful. Perhaps it is only a matter of adding a more few examples? For instance, if I found something like this, it would have been helpful:

from decouple import Config, RepositoryEnv
 
keypath = os.path.join(os.path.dirname(BASE_DIR), 'keys/.env')
config = Config(RepositoryEnv(keypath))
SECRET_KEY = config('SECRET_KEY')

AttributeError: 'tuple' object has no attribute 'read'

REDIS_HOSTS in Django expects a list of tuples.

When I try to do this:

config('REDIS_URL', default=('127.0.0.1', 6379), cast=Csv(post_process=tuple))

I can't start my server, and this error is shown:

AttributeError: 'tuple' object has no attribute 'read'

not finding settings file causes stack overflow on windows

OS: Windows 7 64 Bit

Traceback (most recent call last):
  File "_pydevd_bundle\pydevd_cython_win32_35_64.pyx", line 1025, in _pydevd_bundle.pydevd_cython_win32_35_64.ThreadTracer.__call__ (_pydevd_bundle/pydevd_cython_win32_35_64.c:20043)
RecursionError: maximum recursion depth exceeded
Fatal Python error: Cannot recover from stack overflow.

Thread 0x00000fd8 (most recent call first):
  File "D:\Programme\PyCharm Community Edition 2017.2.3\helpers\pydev\pydevd.py", line 98 in _on_run
  File "D:\Programme\PyCharm Community Edition 2017.2.3\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 294 in run
  File "D:\Programme\Python\Python35\lib\threading.py", line 914 in _bootstrap_inner
  File "D:\Programme\Python\Python35\lib\threading.py", line 882 in _bootstrap

Thread 0x00001bd8 (most recent call first):
  File "D:\Programme\PyCharm Community Edition 2017.2.3\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 356 in _on_run
  File "D:\Programme\PyCharm Community Edition 2017.2.3\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 294 in run
  File "D:\Programme\Python\Python35\lib\threading.py", line 914 in _bootstrap_inner
  File "D:\Programme\Python\Python35\lib\threading.py", line 882 in _bootstrap

Thread 0x00001bd0 (most recent call first):
  File "D:\Programme\Python\Python35\lib\threading.py", line 297 in wait
  File "D:\Programme\Python\Python35\lib\queue.py", line 173 in get
  File "D:\Programme\PyCharm Community Edition 2017.2.3\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 433 in _on_run
  File "D:\Programme\PyCharm Community Edition 2017.2.3\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 294 in run
  File "D:\Programme\Python\Python35\lib\threading.py", line 914 in _bootstrap_inner
  File "D:\Programme\Python\Python35\lib\threading.py", line 882 in _bootstrap

Current thread 0x00000950 (most recent call first):
  File "D:\Programme\Python\Python35\lib\ntpath.py", line 35 in _get_bothseps
  File "D:\Programme\Python\Python35\lib\ntpath.py", line 203 in split
  File "D:\Programme\Python\Python35\lib\ntpath.py", line 239 in dirname
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 170 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  File "C:\Users\user\virtenv\project\lib\site-packages\decouple.py", line 172 in _find_file
  ...

[Feature Request] Port find_dotenv() feature from python-dotenv

python-dotenv has a feature called find_dotenv(), which -

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

This is specially useful for zipapps made using shiv. I don't want to package my sensitve info inside the zipapp, but python-decouple can't find my .env when I run the zipapp, because it only looks at the __file__, not current directory. (correct me if i'm wrong)

Thanks!

(Optionally) Remove settings from environment when reading them

What:
I think it might be interesting to offer the possibility of removing values from the environment at reading time.

Why:
In the case environment variables would be used for secret values, this would help make sure children processes do not receive those secrets. This may also help when the environment is dumped (e.g. I believe Sentry stores a copy of the process environment alongside event, though it does a bit of scrubbing). It may limit the case of involuntary secret exposure. Note: I think this does not change /proc/self/environ, so it's not about securing against a malicious subprocess (that being said, if subprocess is launched as a different user, that might still help).

How:
https://docs.python.org/3.8/library/os.html#os.environ

If the platform supports the unsetenv() function, you can delete items in this mapping to unset environment variables. unsetenv() will be called automatically when an item is deleted from os.environ, and when one of the pop() or clear() methods is called.

Who:
If you think this can be interesting, I may be available to try and make a PR.

How to use decouple with a regular python library?

I am using decouple with a library I am developing.
It works just fine when I am testing it from the development directory. However after I install the library and try to import it from a python process started on a arbitrary directory, it cannot find the settings.ini even if I copy it to the directory I am starting python from.

In such cases, where should the settings.ini be placed? this should be made clear in the documentation.

Is this project still mantained?

Since last commit appears to be at least 2 years old, should I consider this project abandoned? I ask because some PRs not merged are interesting and I would use those features right away (Docker secrets being a must one right now).

How to loop through python-decouple variables?

.env file

####### DJANGO PARAMETERS ################
SECRET_KEY=MY_KEY
DEBUG=True
#### FILE   ## NAMES ##############
DOWNLOAD_FILE=document.pdf
HTML_FILE_PATH=mydoc.htm
EXCEL_FILE_PATH=AM2.CSV
######## USER VALIDATION ##########
USER_VALID_CHECK=mathsishard
####### COLUMNS #########
COLUMNS="NAME,AD1,AD2,AD3,AD4,AD5,HOL,ENT,JT1,JT2"
####### TAGS #####
TAG1_IN_DOC_SAVED_AS_HTML=AD1
TAG2_IN_DOC_SAVED_AS_HTML=AD2
.
.
.
TAGN_IN_DOC_SAVED_AS_HTML=JT2

Now the problem is that I have a django project where I' m taking some user data through a form, using a .html file(which is a word docx saved as .html with {{ }} django tags). Further I have a csv file(database for now). In the TAGS section(plz see above) I will tell the user to put all the names of the {{ }} which he has put in the .docx file. Now my job becomes to just read the .html file and substitute all template tags with the variables by using django's render_to_string.
Problems:
First:
How to loop over all tags underneath TAGS section like a dict?

Second:
If that's not possible how to make 2 .env files(with different names obviously)?

Please help, I'm stuck badly.

Config Import Error

Hello, when I try to use decouple in a project, I got the following error:

 from decouple import config

Error:

ImportError: cannot import name 'config' from 'decouple' (~/.local/lib/python3.7/site-packages/decouple/__init__.py)

I'm not sure what went wrong. Thanks!

UTF-8 support in .env

Hi, first of all, thanks for nice library! It's awesome software 👍
I need to use UTF-8 in .env and want to use python-decouple for this, but I suppose it won't work with UTF-8, because it doesn't pass encoding param to open function:
https://github.com/henriquebastos/python-decouple/blob/24cc51c7880b79d81b5a75c2d441a41428792aa6/decouple.py#L125
So it will fallback to locale.getpreferredencoding(), as specified in Python docs, which is platform dependent. On my Windows 10 its cp1251, but I want to write UTF-8 in .env and be sure it will be read correctly by python-decouple.
It would be nice, if we could pass encoding kwarg to config() and it will be used in open() (or default if this kwarg is not specified)
Feel free to notify me, if I can help with something

= Symbol Breaking Decouple

Using decouple with a Django project to separate passwords from my settings.py file. I've got an element in a .env file, i.e:
SECRET_KEY=myrandompassword124$%=

Which returns:
raise UndefinedValueError('{} not found. Declare it as envvar or define a default value.'.format(option)) decouple.UndefinedValueError: SECRET_KEY not found. Declare it as envvar or define a default value.

I'm guessing the equals sign at the end of the string is throwing decouple for a loop. Any ideas on how to fix? I'd prefer not to have to change the Django key.

add changelog and tags for 3.2 and 3.3 releases

There's no git tag for the 3.2 and 3.3 releases that happened in November. Can you add/push the git tags?

Also, there's nothing in the CHANGELOG about what changed.

Based on commits, I think the changes are as follows:

python-decouple 3.2:

  • fixed typos in documentation
  • add editorconfig file
  • fix handling for configuration values that end in single or double quotes (#78)
  • add support for encoding env files in other encodings (#62) (#64)
  • improve csv documentation (#59)

python-decouple 3.3:

  • enforce search order in settings files
  • switch to using strtobool (#71)

Thank you!

Deprecated use of readfp

Hello,

I get warnings about ConfigParser.reafp when using decouple :

[...]\py3.7\lib\site-packages\decouple.py:106: DeprecationWarning: This method will be removed in future versions. Use 'parser.read_file()' instead.
self.parser.readfp(file_)

The warning message suggests using .read_file() instead.

Option to set a prefix to environment variables

Hey, thanks for this amazing library.

I think that a option to set a prefix to environment variables is missing. You see, sometimes we have so many projects deployed on a single machine, that requires some settings between them.

Perhaps I can use Docker to handle this, but Docker requires a kernel upgrade.

Since this repository does not have a CONTRIBUTING.md, can I implement my needs and submit a PR?

Changelog

The project have a changelog file?

If not, I suggest that create one to facilitate the follow-up of version updates.

how does it work when deploying on heroku ?

I am using it in my django project. Runs perfect on my local machine.

But it is causing problems when deploying on heroku,

Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    execute_from_command_line(sys.argv)
  File "/app/.heroku/python/lib/python3.6/site-packages/django/core/management/__init__.py", line 364, in execute_from_command_line
    utility.execute()
  File "/app/.heroku/python/lib/python3.6/site-packages/django/core/management/__init__.py", line 308, in execute
    settings.INSTALLED_APPS
  File "/app/.heroku/python/lib/python3.6/site-packages/django/conf/__init__.py", line 56, in __getattr__
    self._setup(name)
  File "/app/.heroku/python/lib/python3.6/site-packages/django/conf/__init__.py", line 41, in _setup
    self._wrapped = Settings(settings_module)
  File "/app/.heroku/python/lib/python3.6/site-packages/django/conf/__init__.py", line 110, in __init__
mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/app/.heroku/python/lib/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 978, in _gcd_import
  File "<frozen importlib._bootstrap>", line 961, in _find_and_load
  File "<frozen importlib._bootstrap>", line 950, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
  File "/app/weather/settings.py", line 20, in <module>
    API_KEY = config('API_KEY')
  File "/app/.heroku/python/lib/python3.6/site-packages/decouple.py", line 197, in __call__
     return self.config(*args, **kwargs)
  File "/app/.heroku/python/lib/python3.6/site-packages/decouple.py", line 85, in __call__
    return self.get(*args, **kwargs)
  File "/app/.heroku/python/lib/python3.6/site-packages/decouple.py", line 70, in get
    raise UndefinedValueError('{} not found. Declare it as envvar or define a default value.'.format(option))
decouple.UndefinedValueError: API_KEY not found. Declare it as envvar or define a default value.

I am getting this error when running heroku run python manage.py migrate

What should be done ?

Settings encryption

Any idea/suggestion on how to effectively encrypt settings kept in the .env file, and maybe add a sort of checksum to be "reasonably" sure they haven't been tampered ?

Fix the difference between the way python and ipython do recursive searches.

Assuming that the intended behaviour is to recursively searches up your configuration module path looking for a settings.ini or a .env file, given the following directory tree:

  • settings.ini
  • code.py
  • folder
    • deeper_code.py

with the following content:

[settings]
SOME_VAR = value
# code.py
from decouple import config

print(config('SOME_VAR'))
# deeper_code.py
from decouple import config

print(config('SOME_VAR'))

I've tried:

$ ipython code.py
value
$ python code.py
value
$ ipython folder/deeper_code.py
value
$ python folder/deeper_code.py
Traceback (most recent call last):
  File "folder/deeper_code.py", line 3, in <module>
    print(config('SOME_VAR'))
  File "~/.virtualenvs/decouple-test/lib/python3.4/site-packages/decouple.py", line 197, in __call__
    return self.config(*args, **kwargs)
  File "~/.virtualenvs/decouple-test/lib/python3.4/site-packages/decouple.py", line 77, in __call__
    return self.get(*args, **kwargs)
  File "~/.virtualenvs/decouple-test/lib/python3.4/site-packages/decouple.py", line 64, in get
    raise UndefinedValueError('%s option not found and default value was not defined.' % option)
decouple.UndefinedValueError: SOME_VAR option not found and default value was not defined.

running ipython 4.0.0 over python 3.4.0 and python 3.4.0 alone.

Looking for some explanations, I've seen that ipython and python have a different return for the magic frame.f_back.f_back.f_code.co_filename. The first returns an absolute path and the second a relative one.

So, as the magic depends on a function that should only be used internally, and given the behaviour described, could we replace it for something like path = os.path.abspath('.') or add path = os.path.abspath(path)? If so, I will be happy to offer a pull request.

Quotation Marks on values

Trying to set value to a password email, the character "quotation mark" ( ' ) is ignore. I have research a lot but i couldn't find how to get it around

For example:

On .env:

mySMTPEmail=mypassword'

The quote character at the end of password value is ignored. I've tryied double it for closing, but anyway, when i run debugg the password value doesn't get the quote, like:

mySMTPEmail=mypassword (without quote)

So, can't authenticate on SMTP server

Outdated Csv example on the PyPI and in docstring

Hello. Something that I encountered today while adding HTTPS to my Django project:

On the python-decouple PyPI site, in the understanding the CAST argument section, the first snippet shows a Csv(tuple_=True) class initialization, which is outdated and should be changed to Csv(post_process=tuple).

In the decouple.py file, the constructor docstring has a nonexistent tuple_ parameter described, and there is no description of the post_process parameter.

Compatibility problems with ipython notebook

Hi there,

I'm have some problems when trying integrate ipython notebook on my decouple using django project. I'm using a virtualenv, so... the problem is my manage.py shell run just fine, decouple recover the values from .env file like charm, but when I'm using ipython notebook I'm getting this exception:

/home/nunesvictor/dev/sisref/env/local/lib/python2.7/site-packages/decouple.pyc in get(self, option, default, cast)
62
63 if isinstance(value, Undefined):
---> 64 raise UndefinedValueError('%s option not found and default value was not defined.' % option)
65
66 if isinstance(cast, Undefined):

UndefinedValueError: SECRET_KEY option not found and default value was not defined.```

My settings.py is configured like these:

SECRET_KEY = config('SECRET_KEY')

I wonder why decouple is incapable to get the value of my SECRET_KEY of the .env file, is this really an issue, or I'm just getting the configuration wrong?! Tks

PS. I'm using django_notebook (https://github.com/benthomasson/django_notebook)

Empty env variable raises KeyError

I've created a .env file with:

FOO=

... and when I try to get FOO decouple raises a KeyError exception:

$ cat .env
FOO=
BAR=1
>>> from decouple import config
>>> config("BAR")
'1'
>>> config("BAZ", "undefined")
'undefined'
>>> config("FOO", "undefined")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "decouple.py", line 190, in __call__
    return self.config(*args, **kwargs)
  File "decouple.py", line 73, in __call__
    return self.get(*args, **kwargs)
  File "decouple.py", line 55, in get
    value = self.repository.get(option)
  File "decouple.py", line 123, in get
    return self.data.get(key) or os.environ[key]
  File "/home/osantana/.virtualenvs/opinious/lib/python2.7/UserDict.py", line 23, in __getitem__
    raise KeyError(key)
KeyError: 'FOO'

Dúvida em relação ao arquivo .ini ou .env

Fala Henrique, blz cara?! Cara, estou com uma dúvida em relação ao python-decouple e queria ver se você poderia responder. Minha dúvida é a seguinte: Usando o decouple vou ter minhas variáveis setadas no .ini ou .env e o decouple vai acessar esse arquivo e pegar esses valores. Até ai tudo bem, mas esse arquivo não é comitado, certo?! Nesse caso, ele ficaria na minha máquina. Como faz quando um novo dev vai desenvolver? E se eu perder esse arquivo (formatar a máquina, der algum pau sei lá). Pode parecer idiota a pergunta mas é uma pergunta que não consegui sanar. Desde já agradeço. Abraço

Infer cast if default is given

I simply want to put the idea on the table. If default is set and cast is not, maybe we could default to use cast = type(default)?

Before:

DEBUG = config('DEBUG', default=False, cast=bool)
EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)

After:

DEBUG = config('DEBUG', default=False)
EMAIL_PORT = config('EMAIL_PORT', default=25)

Toggling off the feature would be done by setting default=str. But I guess this would be a breaking change. Another disadvantage is that explicit is better than implicit like the Python's Zen says. Maybe it's not clear that it will use int if we remove cast, for example.

I'm not happy with the idea so I'm going to close it to remove noise.

About getting key error in foolowing command

I am getting key error in following command ...what is the alternative option for it..or else how should correct it ??
import os
COMPUTER_NAME = os.environ['COMPUTERNAME']
print("Computer: ", COMPUTER_NAME)

Support for dict as a cast argument

Hey,

I was wondering what are your thoughts about extending current functionality with a possible "cast as dict" feature.

In Django for example, the 'DATABASES' setting varies from deploy to production and it is quite nice and easy (although I admit, a little brute) to just keep the entire db dict as a string in .env file

This fix makes it possible to just do:

in .env:

DATABASES={'default': {'ENGINE': 'db.backend', 'NAME': '/absolute/path/to/your/db.sqlite3',}} 

and in settings.py:

DATABASES = config('DATABASES', cast=dict)

I've forked the repo and implemented the change (using literal_eval function) so am ready for PR.

What do you think?

empty list as default for Csv() parser.

Hi @henriquebastos,

when using
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[], cast=Csv())

I got this error:

./manage.py runserver 

# ...

File "/Users/rodrigo/workspace/pmpr/megaforce/settings.py", line 30, in <module>
    ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[], cast=Csv())
  File "/Users/rodrigo/.ve/pmpr/lib/python3.6/site-packages/decouple.py", line 197, in __call__
    return self.config(*args, **kwargs)
  File "/Users/rodrigo/.ve/pmpr/lib/python3.6/site-packages/decouple.py", line 77, in __call__
    return self.get(*args, **kwargs)
  File "/Users/rodrigo/.ve/pmpr/lib/python3.6/site-packages/decouple.py", line 71, in get
    return cast(value)
  File "/Users/rodrigo/.ve/pmpr/lib/python3.6/site-packages/decouple.py", line 231, in __call__
    return [transform(s) for s in splitter]
  File "/Users/rodrigo/.ve/pmpr/lib/python3.6/site-packages/decouple.py", line 231, in <listcomp>
    return [transform(s) for s in splitter]
  File "/Users/rodrigo/.pyenv/versions/3.6.1/lib/python3.6/shlex.py", line 295, in __next__
    token = self.get_token()
  File "/Users/rodrigo/.pyenv/versions/3.6.1/lib/python3.6/shlex.py", line 105, in get_token
    raw = self.read_token()
  File "/Users/rodrigo/.pyenv/versions/3.6.1/lib/python3.6/shlex.py", line 136, in read_token
    nextchar = self.instream.read(1)
AttributeError: 'list' object has no attribute 'read'

On the other hand, if I put ALLOWED_HOSTS=127.0.0.1 within my .env file, everything works fine!

Do you have any ideia what's wrong?

An option to specify .env filename

Hi. Similarly to django-configurations, it could be very convenient to be able to specify an environment filename with variables, not just to have .env hard-coded. For example, I could therefore specify a set of vars for different environments, e.g. prod.env, staging.env, and I could switch them using another env variable, e.g.:

DECOUPLE_CONFIGURATION=staging.env python program.py

Conditional required variables proposal

Being able to set defaults is very good since I can set a local development configuration that does not require any effort from new developers to start working on the project. But Id like to be able to tell that some variables must be set in a production environment.

Here is a solution proposal that does not break the current design:

from decouple import config

DEBUG = config('DEBUG', cast=bool)
THIS_IS_REQUIRED = config('THIS_IS_REQUIRED', default='local-value', required_if=(not DEBUG))
THIS_IS_NOT_REQUIRED = config('THIS_IS_NOT_REQUIRED', default='local-value')

In this example situation, if DEBUG is False and the variable THIS_IS_REQUIRED is not set, UndefinedValueError should be raised.

Config.get returning default value without cast(value)

Wouldn't make more sense to return default value in the way it was defined?

For example, in Django, we know that ALLOWED_HOSTS is a list, but if we call config with default=[], it returns an error (using the example code in README):

ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[],
                       cast=lambda v: [s.strip() for s in v.split(',')])

Cannot set default value for SECURE_PROXY_SSL_HEADER

In my settings file I have:

SECURE_PROXY_SSL_HEADER = config('SECURE_PROXY_SSL_HEADER', default=None, cast=Csv(post_process=tuple))

Now, when I want to set the header on one of my projects, everything is fine. I just put the following in my .env file:

SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO, https

However, when I do not need this header in another project, it does not work. Because if I leave empty, the default value is None and that conflicts with cast=Csv(post_process=tuple).

Environment variables must override .env settings

In the Unix world settings and application parameters are usually evaluated in the following order:

  1. CLI arguments (eg. python -u)
  2. Environment variables (eg. PYTHONUNBUFFERED=1)
  3. Local settings files (eg. ./project/setup.cfg)
  4. User settings files (eg. $HOME/.setup.cfg)
  5. System settings files (eg. /etc/setup.cfg)

But python-decouple change this order and use the .env file settings even if we've a envvar with diferent value:

https://github.com/henriquebastos/python-decouple/blob/master/decouple.py#L127-L131

I would like to propose the following (backward incompatible) change for the method above:

    def get(self, key):
        try:
            return os.environ[key]
        except KeyError:
            return self.data[key]

decouple get .env file from two parent folders of the project.

I do not have a .env file in the project folder, but I have one in a folder at the top parent directory outside of project folder. Decouple are reading this file.

/home/user/repository/
    | .env # decouple should not read this file
    | github/
        | projects/
            | myproject /
                | manage.py
                | myproject/
                    | settings.py
                    | urls.py
                    | wsgi.py
                | app1/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.