Compare commits

...

9 Commits

Author SHA1 Message Date
kennethreitz c77a1877d3 v98 2017-02-15 13:49:08 -05:00
kennethreitz 1c51f5d84e NLTK support 2017-02-15 13:44:31 -05:00
kennethreitz 6922a82536 Update README.md 2017-02-01 15:12:21 -05:00
kennethreitz 9cc5bf1a85 Update README.md 2017-02-01 15:11:25 -05:00
kennethreitz 012cb8a4df Added more tests 2017-02-01 15:09:49 -05:00
kennethreitz fab60ae6ab more tests 2017-01-24 23:16:36 -05:00
kennethreitz cd52da6155 tell travis to run the tests 2017-01-24 23:16:36 -05:00
David Zülke acd9347930 Handle and produce leading 0 in WEB_CONCURRENCY (#355)
The Node buildpack now exports a leading zero in its numbers. This lets us detect that it (or another buildpack) set the value, and overwrite it accordingly.

Fixes the issue where adding the node buildpack to a Python app would cause only one gunicorn worker to be spawned for a 1X dyno, and not two.

We also need to again produce leading zeroes in the value, so that e.g. the PHP buildpack can do the same on boot.
2017-01-23 01:29:02 -05:00
kennethreitz d7e2f0fb08 v97 2017-01-02 11:50:49 -05:00
19 changed files with 1868 additions and 14 deletions
+6 -2
View File
@@ -1,2 +1,6 @@
sudo: false
script: exit 0
language: bash
sudo: required
services:
- docker
install: docker pull heroku/cedar:14
script: make test
+16 -5
View File
@@ -1,12 +1,23 @@
# Python Buildpack Changelog
## 98
Official NLTK support and other improvements.
- Support for `nltk.txt` file for declaring corpora to be downloaded.
- Leading zeros for auto-set WEB_CONCURRENCY.
## 97
Improved egg-link functionality.
## 96
Bugfix.
## 95
Improved output support.
Improved output support.
## v94
@@ -18,7 +29,7 @@ Improved support for PyPy.
## v92
Improved cache functionality and fix egg-links regression.
Improved cache functionality and fix egg-links regression.
## v91
@@ -30,11 +41,11 @@ Bugfix.
## v89
Improved cache functionality and fix egg-links regression.
Improved cache functionality and fix egg-links regression.
## v88
Fixed bug with editable pip installations.
Fixed bug with editable pip installations.
## v87
@@ -49,7 +60,7 @@ Refactor and multi-buildpack compatibility.
## v85
Packaging fix.
Packaging fix.
## v84
+6 -2
View File
@@ -1,8 +1,12 @@
# These targets are not files
.PHONY: tests
tests:
./bin/test
test: test-cedar-14
test-cedar-14:
@echo "Running tests in docker (cedar-14)..."
@docker run -v $(shell pwd):/buildpack:ro --rm -it -e "STACK=cedar-14" heroku/cedar:14 bash -c 'cp -r /buildpack /buildpack_test; cd /buildpack_test/; test/run;'
@echo ""
tools:
git clone https://github.com/kennethreitz/pip-pop.git
+2
View File
@@ -2,6 +2,8 @@
# Heroku Buildpack: Python
[![Build Status](https://travis-ci.org/heroku/heroku-buildpack-python.svg?branch=master)](https://travis-ci.org/heroku/heroku-buildpack-python)
This is the official [Heroku buildpack](https://devcenter.heroku.com/articles/buildpacks) for Python apps, powered by [pip](https://pip.pypa.io/) and other excellent software.
Recommended web frameworks include **Django** and **Flask**. The recommended webserver is **Gunicorn**. There are no restrictions around what software can be used (as long as it's pip-installable). Web processes must bind to `$PORT`, and only the HTTP protocol is permitted for incoming connections.
+3
View File
@@ -193,6 +193,9 @@ source $BIN_DIR/steps/gdal
# Install dependencies with Pip (where the magic happens).
source $BIN_DIR/steps/pip-install
# Support for NLTK corpora.
sub-env $BIN_DIR/steps/nltk
# Support for pip install -e.
rm -fr $BUILD_DIR/.heroku/src
deep-cp /app/.heroku/src $BUILD_DIR/.heroku/src
Executable
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# This script serves as the NLTK build step of the
# [**Python Buildpack**](https://github.com/heroku/heroku-buildpack-python)
# compiler.
#
# A [buildpack](https://devcenter.heroku.com/articles/buildpacks) is an
# adapter between a Python application and Heroku's runtime.
#
# This script is invoked by [`bin/compile`](/).
# Syntax sugar.
source $BIN_DIR/utils
bpwatch start nltk_download
export NLTK_DATA_DIR="$BUILD_DIR/nltk_data"
export NLTK_DATA="$BUILD_DIR/nltk_data"
# Check that nltk was installed by pip, otherwise obviously not needed
python -m nltk.downloader -h >/dev/null 2>&1
if [ $? -eq 0 ]; then
puts-step "Downloading NLTK corpora..."
nltk_packages_definition="$BUILD_DIR/nltk.txt"
if [ -f "$nltk_packages_definition" ]; then
nltk_packages=$(tr "\n" " " < "$nltk_packages_definition")
puts-step "Downloading NLTK packages: $nltk_packages"
python -m nltk.downloader -d $BUILD_DIR/.heroku/python/nltk_data $nltk_packages | indent
set-env NLTK_DATA "/app/.heroku/python/nltk_data"
else
puts-warn "nltk.txt not found, not downloading any corpora"
fi
else
puts-warn "nltk not apparently installed, not downloading packages"
fi
bpwatch stop nltk_download
+1
View File
@@ -0,0 +1 @@
psycopg2
+1
View File
@@ -0,0 +1 @@
requests
+1
View File
@@ -0,0 +1 @@
python-2.7.13
+1
View File
@@ -0,0 +1 @@
requests
+1
View File
@@ -0,0 +1 @@
python-3.6.0
+1
View File
@@ -0,0 +1 @@
requests
+114
View File
@@ -0,0 +1,114 @@
Maya: Datetime for Humans™
==========================
.. image:: https://img.shields.io/pypi/v/maya.svg
:target: https://pypi.python.org/pypi/maya
.. image:: https://travis-ci.org/kennethreitz/maya.svg?branch=master
:target: https://travis-ci.org/kennethreitz/maya
.. image:: https://img.shields.io/badge/SayThanks.io-☼-1EAEDB.svg
:target: https://saythanks.io/to/kennethreitz
Datetimes are very frustrating to work with in Python, especially when dealing
with different locales on different systems. This library exists to make the
simple things **much** easier, while admitting that time is an illusion
(timezones doubly so).
Datetimes should be interacted with via an API written for humans.
Maya is mostly built around the headaches and use-cases around parsing datetime data from websites.
☤ Basic Usage of Maya
---------------------
Behold, datetimes for humans!
.. code-block:: pycon
>>> now = maya.now()
<MayaDT epoch=1481850660.9>
>>> tomorrow = maya.when('tomorrow')
<MayaDT epoch=1481919067.23>
>>> tomorrow.slang_date()
'tomorrow'
>>> tomorrow.slang_time()
'23 hours from now'
>>> tomorrow.iso8601()
'2016-12-16T15:11:30.263350Z'
>>> tomorrow.rfc2822()
'Fri, 16 Dec 2016 20:11:30 -0000'
>>> tomorrow.datetime()
datetime.datetime(2016, 12, 16, 15, 11, 30, 263350, tzinfo=<UTC>)
# Automatically parse datetime strings and generate naive datetimes.
>>> scraped = '2016-12-16 18:23:45.423992+00:00'
>>> maya.parse(scraped).datetime(to_timezone='US/Eastern', naive=True)
datetime.datetime(2016, 12, 16, 13, 23, 45, 423992)
>>> rand_day = maya.when('2011-02-07', timezone='US/Eastern')
<MayaDT epoch=1297036800.0>
# Note how this is the 6th, not the 7th.
>>> rand_day.day
6
# Always.
>>> rand_day.timezone
UTC
☤ Why is this useful?
---------------------
- All timezone algebra will behave identically on all machines, regardless of system locale.
- Complete symmetric import and export of both ISO 8601 and RFC 2822 datetime stamps.
- Fantastic parsing of both dates written for/by humans and machines (``maya.when()`` vs ``maya.parse()``).
- Support for human slang, both import and export (e.g. `an hour ago`).
- Datetimes can very easily be generated, with or without tzinfo attached.
- This library is based around epoch time, but dates before Jan 1 1970 are indeed supported, via negative integers.
- Maya never panics, and always carries a towel.
☤ What about Delorean, Arrow, & Pendulum?
-----------------------------------------
Arrow, for example, is a fantastic library, but isn't what I wanted in a datetime library. In many ways, it's better than Maya for certain things. In some ways, in my opinion, it's not.
I simply desire a sane API for datetimes that made sense to me for all the things I'd ever want to do—especially when dealing with timezone algebra. Arrow doesn't do all of the things I need (but it does a lot more!). Maya does do exactly what I need.
I think these projects complement each-other, personally. Maya is great for parsing websites. For example- Arrow supports floors and ceilings and spans of dates, which Maya does not at all.
☤ Installing Maya
-----------------
Installation is easy, with pip::
$ pip install maya
✨🍰✨
☤ Like it?
----------
`Say Thanks <https://saythanks.io/to/kennethreitz>`_!
How to Contribute
-----------------
#. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
#. Fork `the repository`_ on GitHub to start making your changes to the **master** branch (or branch off of it).
#. Write a test which shows that the bug was fixed or that the feature works as expected.
#. Send a pull request and bug the maintainer until it gets merged and published. :) Make sure to add yourself to AUTHORS_.
.. _`the repository`: http://github.com/kennethreitz/maya
.. _AUTHORS: https://github.com/kennethreitz/maya/blob/master/AUTHORS.rst
+273
View File
@@ -0,0 +1,273 @@
# ___ __ ___ _ _ ___
# || \/ | ||=|| \\// ||=||
# || | || || // || ||
# Ignore warnings for yaml usage.
import warnings
import ruamel.yaml
warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
import email.utils
import time
from datetime import datetime as Datetime
import pytz
import humanize
import dateparser
import iso8601
import dateutil.parser
from tzlocal import get_localzone
_EPOCH_START = (1970, 1, 1)
def validate_class_type_arguments(operator):
"""
Decorator to validate all the arguments to function
are of the type of calling class
"""
def inner(function):
def wrapper(self, *args, **kwargs):
for arg in args + tuple(kwargs.values()):
if not isinstance(arg, self.__class__):
raise TypeError('unorderable types: {}() {} {}()'.format(
type(self).__name__, operator, type(arg).__name__))
return function(self, *args, **kwargs)
return wrapper
return inner
class MayaDT(object):
"""The Maya Datetime object."""
def __init__(self, epoch):
super(MayaDT, self).__init__()
self._epoch = epoch
def __repr__(self):
return '<MayaDT epoch={}>'.format(self._epoch)
def __str__(self):
return self.rfc2822()
def __format__(self, *args, **kwargs):
"""Return's the datetime's format"""
return format(self.datetime(), *args, **kwargs)
@validate_class_type_arguments('==')
def __eq__(self, maya_dt):
return self._epoch == maya_dt._epoch
@validate_class_type_arguments('!=')
def __ne__(self, maya_dt):
return self._epoch != maya_dt._epoch
@validate_class_type_arguments('<')
def __lt__(self, maya_dt):
return self._epoch < maya_dt._epoch
@validate_class_type_arguments('<=')
def __le__(self, maya_dt):
return self._epoch <= maya_dt._epoch
@validate_class_type_arguments('>')
def __gt__(self, maya_dt):
return self._epoch > maya_dt._epoch
@validate_class_type_arguments('>=')
def __ge__(self, maya_dt):
return self._epoch >= maya_dt._epoch
# Timezone Crap
# -------------
@property
def timezone(self):
"""Returns the UTC tzinfo name. It's always UTC. Always."""
return 'UTC'
@property
def _tz(self):
"""Returns the UTC tzinfo object."""
return pytz.timezone(self.timezone)
@property
def local_timezone(self):
"""Returns the name of the local timezone, for informational purposes."""
return self._local_tz.zone
@property
def _local_tz(self):
"""Returns the local timezone."""
return get_localzone()
@staticmethod
def __dt_to_epoch(dt):
"""Converts a datetime into an epoch."""
# Assume UTC if no datetime is provided.
if dt.tzinfo is None:
dt = dt.replace(tzinfo=pytz.utc)
epoch_start = Datetime(*_EPOCH_START, tzinfo=pytz.timezone('UTC'))
return (dt - epoch_start).total_seconds()
# Importers
# ---------
@classmethod
def from_datetime(klass, dt):
"""Returns MayaDT instance from datetime."""
return klass(klass.__dt_to_epoch(dt))
@classmethod
def from_iso8601(klass, string):
"""Returns MayaDT instance from iso8601 string."""
dt = iso8601.parse_date(string)
return klass.from_datetime(dt)
@staticmethod
def from_rfc2822(string):
"""Returns MayaDT instance from rfc2822 string."""
return parse(string)
# Exporters
# ---------
def datetime(self, to_timezone=None, naive=False):
"""Returns a timezone-aware datetime...
Defaulting to UTC (as it should).
Keyword Arguments:
to_timezone {string} -- timezone to convert to (default: None/UTC)
naive {boolean} -- if True, the tzinfo is simply dropped (default: False)
"""
if to_timezone:
dt = self.datetime().astimezone(pytz.timezone(to_timezone))
else:
dt = Datetime.utcfromtimestamp(self._epoch)
dt.replace(tzinfo=self._tz)
# Strip the timezone info if requested to do so.
if naive:
return dt.replace(tzinfo=None)
else:
if dt.tzinfo is None:
dt = dt.replace(tzinfo=self._tz)
return dt
def iso8601(self):
"""Returns an ISO 8601 representation of the MayaDT."""
# Get a timezone-naive datetime.
dt = self.datetime(naive=True)
return '{}Z'.format(dt.isoformat())
def rfc2822(self):
"""Returns an RFC 2822 representation of the MayaDT."""
return email.utils.formatdate(self.epoch, usegmt=True)
# Properties
# ----------
@property
def year(self):
return self.datetime().year
@property
def month(self):
return self.datetime().month
@property
def day(self):
return self.datetime().day
@property
def week(self):
return self.datetime().isocalendar()[1]
@property
def weekday(self):
"""Return the day of the week as an integer. Monday is 1 and Sunday is 7"""
return self.datetime().isoweekday()
@property
def hour(self):
return self.datetime().hour
@property
def minute(self):
return self.datetime().minute
@property
def second(self):
return self.datetime().second
@property
def microsecond(self):
return self.datetime().microsecond
@property
def epoch(self):
return self._epoch
# Human Slang Extras
# ------------------
def slang_date(self):
""""Returns human slang representation of date."""
dt = self.datetime(naive=True, to_timezone=self.local_timezone)
return humanize.naturaldate(dt)
def slang_time(self):
""""Returns human slang representation of time."""
dt = self.datetime(naive=True, to_timezone=self.local_timezone)
return humanize.naturaltime(dt)
def now():
"""Returns a MayaDT instance for this exact moment."""
epoch = time.time()
return MayaDT(epoch=epoch)
def when(string, timezone='UTC'):
""""Returns a MayaDT instance for the human moment specified.
Powered by dateparser. Useful for scraping websites.
Examples:
'next week', 'now', 'tomorrow', '300 years ago', 'August 14, 2015'
Keyword Arguments:
string -- string to be parsed
timezone -- timezone referenced from (default: 'UTC')
"""
dt = dateparser.parse(string, settings={'TIMEZONE': timezone, 'RETURN_AS_TIMEZONE_AWARE': True, 'TO_TIMEZONE': 'UTC'})
if dt is None:
raise ValueError('invalid datetime input specified.')
return MayaDT.from_datetime(dt)
def parse(string, day_first=False):
""""Returns a MayaDT instance for the machine-produced moment specified.
Powered by dateutil. Accepts most known formats. Useful for working with data.
Keyword Arguments:
string -- string to be parsed
day_first -- if true, the first value (e.g. 01/05/2016) is parsed as day (default: False)
"""
dt = dateutil.parser.parse(string, dayfirst=day_first)
return MayaDT.from_datetime(dt)
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import codecs
from setuptools import setup
try:
# Python 3
from os import dirname
except ImportError:
# Python 2
from os.path import dirname
here = os.path.abspath(dirname(__file__))
with codecs.open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
long_description = '\n' + f.read()
if sys.argv[-1] == "publish":
os.system("python setup.py sdist bdist_wheel upload")
sys.exit()
required = [
'humanize',
'pytz',
'dateparser',
'iso8601',
'python-dateutil',
'ruamel.yaml',
'tzlocal'
]
setup(
name='maya',
version='0.1.6',
description='Datetimes for Humans.',
long_description=long_description,
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
url='https://github.com/kennethreitz/maya',
py_modules=['maya'],
install_requires=required,
license='MIT',
classifiers=(
),
)
Executable
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
testNoRequirements() {
compile "no-requirements"
assertCapturedError
}
testSetupPy() {
compile "setup-py"
assertCaptured "maya"
assertCapturedSuccess
}
testStandardRequirements() {
compile "requirements-standard"
assertCaptured "requests"
assertCapturedSuccess
}
testPsycopg2() {
compile "psycopg2"
assertCaptured "psycopg2"
assertCapturedSuccess
}
testPython2() {
compile "python2"
assertCaptured "python-2.7.13"
assertCapturedSuccess
}
testPython3() {
compile "python3"
assertCaptured "python-3.6.0"
assertCapturedSuccess
}
pushd $(dirname 0) >/dev/null
popd >/dev/null
source $(pwd)/test/utils
mktmpdir() {
dir=$(mktemp -t testXXXXX)
rm -rf $dir
mkdir $dir
echo $dir
}
detect() {
capture $(pwd)/bin/detect $(pwd)/test/fixtures/$1
}
compile_dir=""
default_process_types_cleanup() {
file="/tmp/default_process_types"
if [ -f "$file" ]; then
rm "$file"
fi
}
compile() {
default_process_types_cleanup
bp_dir=$(mktmpdir)
compile_dir=$(mktmpdir)
cp -a $(pwd)/* ${bp_dir}
cp -a ${bp_dir}/test/fixtures/$1/. ${compile_dir}
capture ${bp_dir}/bin/compile ${compile_dir} ${2:-$(mktmpdir)} $3
}
compileDir() {
default_process_types_cleanup
local bp_dir=$(mktmpdir)
local compile_dir=${1:-$(mktmpdir)}
local cache_dir=${2:-$(mktmpdir)}
local env_dir=$3
cp -a $(pwd)/* ${bp_dir}
capture ${bp_dir}/bin/compile ${compile_dir} ${cache_dir} ${env_dir}
}
release() {
bp_dir=$(mktmpdir)
cp -a $(pwd)/* ${bp_dir}
capture ${bp_dir}/bin/release ${bp_dir}/test/fixtures/$1
}
assertFile() {
assertEquals "$1" "$(cat ${compile_dir}/$2)"
}
source $(pwd)/test/shunit2
Executable
+1048
View File
File diff suppressed because it is too large Load Diff
+195
View File
@@ -0,0 +1,195 @@
#!/bin/sh
# taken from
# https://github.com/ryanbrainard/heroku-buildpack-testrunner/blob/master/lib/test_utils.sh
oneTimeSetUp()
{
TEST_SUITE_CACHE="$(mktemp -d ${SHUNIT_TMPDIR}/test_suite_cache.XXXX)"
}
oneTimeTearDown()
{
rm -rf ${TEST_SUITE_CACHE}
}
setUp()
{
OUTPUT_DIR="$(mktemp -d ${SHUNIT_TMPDIR}/output.XXXX)"
STD_OUT="${OUTPUT_DIR}/stdout"
STD_ERR="${OUTPUT_DIR}/stderr"
BUILD_DIR="${OUTPUT_DIR}/build"
CACHE_DIR="${OUTPUT_DIR}/cache"
mkdir -p ${OUTPUT_DIR}
mkdir -p ${BUILD_DIR}
mkdir -p ${CACHE_DIR}
}
tearDown()
{
rm -rf ${OUTPUT_DIR}
}
capture()
{
resetCapture
LAST_COMMAND="$@"
$@ >${STD_OUT} 2>${STD_ERR}
RETURN=$?
rtrn=${RETURN} # deprecated
}
resetCapture()
{
if [ -f ${STD_OUT} ]; then
rm ${STD_OUT}
fi
if [ -f ${STD_ERR} ]; then
rm ${STD_ERR}
fi
unset LAST_COMMAND
unset RETURN
unset rtrn # deprecated
}
detect()
{
capture ${BUILDPACK_HOME}/bin/detect ${BUILD_DIR}
}
compile()
{
capture ${BUILDPACK_HOME}/bin/compile ${BUILD_DIR} ${CACHE_DIR}
}
release()
{
capture ${BUILDPACK_HOME}/bin/release ${BUILD_DIR}
}
assertCapturedEquals()
{
assertEquals "$@" "$(cat ${STD_OUT})"
}
assertCapturedNotEquals()
{
assertNotEquals "$@" "$(cat ${STD_OUT})"
}
assertCaptured()
{
assertFileContains "$@" "${STD_OUT}"
}
assertNotCaptured()
{
assertFileNotContains "$@" "${STD_OUT}"
}
assertCapturedSuccess()
{
assertEquals "Expected captured exit code to be 0; was <${RETURN}>" "0" "${RETURN}"
assertEquals "Expected STD_ERR to be empty; was <$(cat ${STD_ERR})>" "" "$(cat ${STD_ERR})"
}
# assertCapturedError [[expectedErrorCode] expectedErrorMsg]
assertCapturedError()
{
if [ $# -gt 1 ]; then
local expectedErrorCode=${1}
shift
fi
local expectedErrorMsg=${1:-""}
if [ -z ${expectedErrorCode} ]; then
assertTrue "Expected captured exit code to be greater than 0; was <${RETURN}>" "[ ${RETURN} -gt 0 ]"
else
assertTrue "Expected captured exit code to be <${expectedErrorCode}>; was <${RETURN}>" "[ ${RETURN} -eq ${expectedErrorCode} ]"
fi
if [ "${expectedErrorMsg}" != "" ]; then
assertFileContains "Expected STD_ERR to contain error <${expectedErrorMsg}>" "${expectedErrorMsg}" "${STD_ERR}"
fi
}
_assertContains()
{
if [ 5 -eq $# ]; then
local msg=$1
shift
elif [ ! 4 -eq $# ]; then
fail "Expected 4 or 5 parameters; Receieved $# parameters"
fi
local needle=$1
local haystack=$2
local expectation=$3
local haystack_type=$4
case "${haystack_type}" in
"file") grep -q -F -e "${needle}" ${haystack} ;;
"text") echo "${haystack}" | grep -q -F -e "${needle}" ;;
esac
if [ "${expectation}" != "$?" ]; then
case "${expectation}" in
0) default_msg="Expected <${haystack}> to contain <${needle}>" ;;
1) default_msg="Did not expect <${haystack}> to contain <${needle}>" ;;
esac
fail "${msg:-${default_msg}}"
fi
}
debug()
{
cat $STD_OUT
}
assertContains()
{
_assertContains "$@" 0 "text"
}
assertNotContains()
{
_assertContains "$@" 1 "text"
}
assertFileContains()
{
_assertContains "$@" 0 "file"
}
assertFileNotContains()
{
_assertContains "$@" 1 "file"
}
command_exists () {
type "$1" > /dev/null 2>&1 ;
}
assertFileMD5()
{
expectedHash=$1
filename=$2
if command_exists "md5sum"; then
md5_cmd="md5sum ${filename}"
expected_md5_cmd_output="${expectedHash} ${filename}"
elif command_exists "md5"; then
md5_cmd="md5 ${filename}"
expected_md5_cmd_output="MD5 (${filename}) = ${expectedHash}"
else
fail "no suitable MD5 hashing command found on this system"
fi
assertEquals "${expected_md5_cmd_output}" "`${md5_cmd}`"
}
+11 -5
View File
@@ -1,32 +1,38 @@
if [[ "${WEB_CONCURRENCY:-}" == 0* ]]; then
# another buildpack set a default value, with leading zero
unset WEB_CONCURRENCY
fi
case $(ulimit -u) in
# Automatic configuration for Gunicorn's Workers setting.
# Leading zero padding so a subsequent buildpack can figure out that we set a value, and not the user
# Standard-1X (+Free, +Hobby) Dyno
256)
export DYNO_RAM=512
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-2}
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-02}
;;
# Standard-2X Dyno
512)
export DYNO_RAM=1024
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-4}
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-04}
;;
# Performance-M Dyno
16384)
export DYNO_RAM=2560
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-8}
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-08}
;;
# Performance-L Dyno
32768)
export DYNO_RAM=6656
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-11}
export WEB_CONCURRENCY=${WEB_CONCURRENCY:-011}
;;
esac
# Automatic configuration for Gunicorn's ForwardedAllowIPS setting.
export FORWARDED_ALLOW_IPS='*'
export FORWARDED_ALLOW_IPS='*'