Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Perfect requirements and settings structure

  • Loading branch information...
commit 59e1267fee517d766cd50d3cc4ae38f6781efa90 1 parent 2432324
@jpscaletti jpscaletti authored
Showing with 107 additions and 916 deletions.
  1. +14 −2 CHANGES.md
  2. +2 −3 Makefile
  3. +1 −3 requirements.txt
  4. +1 −1  shake/__init__.py
  5. +46 −12 shake/app.py
  6. +0 −560 shake/i18n.py
  7. +1 −22 shake/render.py
  8. +1 −1  shake/skeletons/project/bundles/common/views.py
  9. +3 −4 shake/skeletons/project/main.py
  10. +2 −0  shake/skeletons/project/requirements.txt
  11. +12 −0 shake/skeletons/project/requirements/common.txt
  12. +2 −1  shake/skeletons/project/{settings/req-dev.txt → requirements/dev.txt}
  13. +2 −1  shake/skeletons/project/{settings/req-prod.txt → requirements/prod.txt}
  14. +9 −0 shake/skeletons/project/requirements/test.txt
  15. +3 −3 shake/skeletons/project/settings/__init__.py
  16. +1 −1  shake/skeletons/project/settings/{base.py.tmpl → common.py.tmpl}
  17. +1 −4 shake/skeletons/project/settings/{development.py → dev.py}
  18. +6 −2 shake/skeletons/project/settings/{production.py → prod.py}
  19. +0 −15 shake/skeletons/project/settings/req.txt
  20. 0  shake/skeletons/project/settings/{testing.py → test.py}
  21. +0 −50 shake/wrappers.py
  22. +0 −7 tests/res/es.yml
  23. +0 −2  tests/res/es_PE.yml
  24. +0 −2  tests/res/sub/es.yml
  25. +0 −220 tests/test_i18n.py
View
16 CHANGES.md
@@ -1,6 +1,20 @@
# Shake Changelog
+## Version 1.2.6
+
+- Perfect requiremnts and settings structure.
+
+- `get_env` and `set_env` search from/set the environment variable `SHAKE_ENV` if no `.SHAKE_ENV` file is present. This is useful for deploying with services like Heroku.
+
+- i18n module extracted to a separated library (now named `AllSpeak`).
+
+
+## Version 1.2.5
+
+- Added `get_timezone` method to `shake.Request`.
+
+
## Version 1.2.4
- Respect the default timezone if a custom one is not defined.
@@ -14,8 +28,6 @@
- Several bugfixes to `Request` class.
-- Added `Moar` thumbnailer to the default project requirements.
-
## Version 1.2.2
View
5 Makefile
@@ -6,7 +6,6 @@ clean: clean-pyc
rm -rf build
rm -rf dist
rm -rf *.egg-info
- rm -rf tests/res/t
find . -name '.DS_Store' -exec rm -f {} \;
find . -name '*~' -exec rm -f {} \;
@@ -15,8 +14,8 @@ clean-pyc:
find . -name '*.pyo' -exec rm -f {} \;
test:
- rm -rf tests/res/t
- python runtests.py tests
+ rm -rf tests/__pycache__
+ py.test tests
rm -rf tests/__pycache__
upload: clean
View
4 requirements.txt
@@ -4,11 +4,9 @@
#
# pip install -r requirements.txt
-babel
+allspeak
inflector
jinja2>=2.4
pyceo>=1.0.3
-pytz
-pyyaml
voodoo>=0.7
werkzeug>=0.7
View
2  shake/__init__.py
@@ -53,5 +53,5 @@ def hello(request):
NotAllowed = Forbidden
redirect_to = redirect
-__version__ = '1.2.5'
+__version__ = '1.2.6'
View
58 shake/app.py
@@ -12,6 +12,7 @@
from os.path import isdir, dirname, join, abspath, normpath, realpath
import socket
+from allspeak import I18n, LOCALES_DIR
from pyceo import Manager
from werkzeug.exceptions import HTTPException, NotFound, BadRequest
from werkzeug.local import LocalManager
@@ -20,7 +21,6 @@
from .config import get_settings_object
from .helpers import local, to_unicode
-from .i18n import I18n, LOCALES_DIR
from .render import Render, TEMPLATES_DIR
from .routes import Map, Rule
from .wrappers import Request, Response, make_response
@@ -35,7 +35,9 @@
SECRET_KEY_MINLEN = 20
STATIC_DIR = 'static'
WELCOME_MESSAGE = "Welcome aboard. You're now using Shake!"
+
ENV_FILE = '.SHAKE_ENV'
+ENV_NAME = 'SHAKE_ENV'
DEFAULT_ENV = 'development'
@@ -123,16 +125,40 @@ def create_default_services(self):
`root_path` as the base path for the `'templates'` and `'locales'` dirs.
"""
+ templates_dir = join(self.root_path, TEMPLATES_DIR)
+ render = Render(templates_dir,
+ default_mimetype=self.settings.get('DEFAULT_MIMETYPE'),
+ response_class=self.response_class)
+
locales_dir = (self.settings.get('LOCALES_DIR') or
join(self.root_path, LOCALES_DIR))
if isinstance(locales_dir, basestring):
locales_dir = [locales_dir]
- self.i18n = I18n(locales_dir, app=self)
-
- templates_dir = join(self.root_path, TEMPLATES_DIR)
- self.render = Render(templates_dir, i18n=self.i18n,
- default_mimetype=self.settings.get('DEFAULT_MIMETYPE'),
- response_class=self.response_class)
+ get_request = lambda: local.request
+
+ i18n = I18n(
+ locales_dirs=locales_dir,
+ get_request=get_request,
+ default_locale=self.settings.DEFAULT_LOCALE,
+ default_timezone=self.settings.DEFAULT_TIMEZONE
+ )
+
+ render.env.globals['t'] = i18n.translate
+ render.env.filters.update({
+ 'format': i18n.format,
+ 'datetimeformat': i18n.format_datetime,
+ 'dateformat': i18n.format_date,
+ 'timeformat': i18n.format_time,
+ 'timedeltaformat': i18n.format_timedelta,
+ 'numberformat': i18n.format_number,
+ 'decimalformat': i18n.format_decimal,
+ 'currencyformat': i18n.format_currency,
+ 'percentformat': i18n.format_percent,
+ 'scientificformat': i18n.format_scientific,
+ })
+
+ self.render = render
+ self.i18n = i18n
def route(self, url, *args, **kwargs):
@@ -508,6 +534,11 @@ def run(self, host=None, port=None, debug=None, reloader=None,
"""
host = host or self.settings.SERVER_NAME
port = port or self.settings.SERVER_PORT
+ try:
+ port = int(port)
+ except (ValueError, TypeError):
+ port = None
+
debug = bool(debug if debug is not None else
self.settings.DEBUG)
reloader = bool(reloader if (reloader is not None) else
@@ -551,8 +582,9 @@ def __call__(self, environ, start_response):
def set_env(env):
- """Set the working environment to `env` saving it in `ENV_FILE`.
- `env` is the name of the new environment eg: 'development' or 'production'.
+ """Set the working environment to `env` saving it in `.SHAKE_ENV`.
+ `env` is the name of the new environment eg: 'development', 'production',
+ 'testing', etc.
You use environments to load different settings for development,
production, testing, etc.
@@ -560,11 +592,13 @@ def set_env(env):
"""
with io.open(ENV_FILE, 'wt') as f:
f.write(to_unicode(env))
+ os.environ[ENV_NAME] = env
return env
def get_env(default=DEFAULT_ENV):
- """Read the current working environment from `ENV_FILE`.
+ """Read the current working environment from `.SHAKE_ENV` or from the
+ environment variable `SHAKE_ENV` .
You use environments to load different settings for development,
production, testing, etc.
@@ -574,8 +608,8 @@ def get_env(default=DEFAULT_ENV):
with io.open(ENV_FILE, 'rt') as f:
env = f.read()
except IOError:
- return default
- return env or default
+ pass
+ return os.environ.get(ENV_NAME) or default
def env_is(env):
View
560 shake/i18n.py
@@ -1,560 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- Shake.i18n
- --------------------------
-
- Implements i18n/l10n support for Shake applications based on the
- awesome Babel and pytz.
-
- ----------------
- Some code derived from Flask-Babel (c) 2010 by Armin Ronacher.
- Used under the modified BSD license.
-
-"""
-from __future__ import absolute_import
-import os
-
-# Workaround for a OSX bug
-if os.environ.get('LC_CTYPE', '').lower() == 'utf-8':
- os.environ['LC_CTYPE'] = 'en_US.utf-8'
-
-import datetime as d
-from decimal import Decimal
-import io
-from os.path import join, dirname, realpath, abspath, normpath, isdir, isfile
-
-from babel import dates, numbers, Locale
-from jinja2 import Markup
-from pytz import timezone, UTC
-from werkzeug import ImmutableDict
-import yaml
-
-from .helpers import local
-
-
-LOCALES_DIR = 'locales'
-
-
-class I18n(object):
- """Internationalization system
-
- """
-
- default_date_formats = ImmutableDict({
- 'time': 'medium',
- 'date': 'medium',
- 'datetime': 'medium',
- 'time.short': None,
- 'time.medium': None,
- 'time.full': None,
- 'time.long': None,
- 'date.short': None,
- 'date.medium': None,
- 'date.full': None,
- 'date.long': None,
- 'datetime.short': None,
- 'datetime.medium': None,
- 'datetime.full': None,
- 'datetime.long': None,
- })
-
-
- def __init__(self, locales_dirs=None, app=None, date_formats=None):
- """
- locales_dirs
- : list of paths that will be searched, in order, for the locales
- app
- : a `Shake` instance.
- date_formats
- : defaults date formats.
-
- """
- self.translations = {}
-
- if isinstance(locales_dirs, basestring):
- locales_dirs = [locales_dirs]
- locales_dirs = locales_dirs or [LOCALES_DIR]
- search_paths = []
- for p in locales_dirs:
- p = normpath(abspath(realpath(p)))
- if not isdir(p):
- p = dirname(p)
- search_paths.append(p)
- self.search_paths = search_paths
- self.date_formats = self.default_date_formats.copy()
- if app:
- self.init_app(app)
-
-
- def init_app(self, app):
- """
- """
- self.app = app
-
-
- @property
- def default_locale(self):
- """The default locale from the configuration as an instance of
- `Babel.Locale`.
-
- """
- locale = self.app.settings.DEFAULT_LOCALE
- if isinstance(locale, tuple):
- return Locale(*locale)
- return Locale.parse(locale)
-
-
- @property
- def default_timezone(self):
- """The default timezone from the configuration as instance of a
- `pytz.timezone` object.
-
- """
- return timezone(self.app.settings.DEFAULT_TIMEZONE)
-
-
- def get_locale(self):
- """Returns the locale that should be used for this request as
- an instance of `Babel.Locale`.
- This returns the default locale if used outside of a request.
-
- """
- return (hasattr(local, 'request') and
- local.request.get_locale(self.default_locale)) or \
- self.default_locale
-
-
- def get_timezone(self):
- """Returns the timezone that should be used for this request as
- `pytz.timezone` object. This returns the default timezone if used
- outside of a request or if no timezone was defined.
-
- """
- return (hasattr(local, 'request') and
- local.request.get_timezone(self.default_timezone)) or \
- self.default_timezone
-
-
- def load_language(self, path, locale):
- """From the given `path`, load the language file for the current or
- given locale. If the locale has a territory attribute (eg: 'US') the
- the specific 'en_US' version will be tried first.
-
- """
- if locale.territory:
- filenames = [str(locale), locale.language]
- else:
- filenames = [locale.language]
- filenames.append(str(self.default_locale))
-
- for filename in filenames:
- cache_key = join(path, filename)
- cached = self.translations.get(cache_key)
- if cached:
- return cached
- filename = cache_key + '.yml'
- if isfile(filename):
- break
- else:
- return
- try:
- with io.open(filename) as f:
- data = yaml.load(f)
- self.translations[cache_key] = data
- return data
- except (IOError, AttributeError):
- return
-
-
- def find_keypath(self, key):
- """Based on the `key`, teturn the path of the language file and the
- subkey inside that file.
-
- """
- if ':' not in key:
- return self.search_paths[0], key
-
- path, subkey = key.split(':', 1)
- lpath = path.split('.')
-
- for root in self.search_paths:
- dirname, dirnames, filenames = os.walk(root).next()
- if lpath[0] in dirnames:
- break
- else:
- return None, None
-
- path = join(root, *lpath)
- if not isdir(path):
- return None, None
- return path, subkey
-
-
- def key_lookup(self, key, locale):
- """
- """
- path, subkey = self.find_keypath(key)
- if not (path and subkey):
- return None
-
- value = self.load_language(path, locale)
- if value is None:
- return None
-
- try:
- for k in subkey.split('.'):
- value = value.get(k)
- if value is None:
- return None
- return value
- except (IndexError, ValueError):
- return None
-
-
- def translate(self, key, count=None, locale=None, **kwargs):
- """Load the translation for the given key using the current locale.
-
- If the value is a dictionary, and `count` is defined, uses the value
- whose key is that number. If that key doesn't exist, a `'n'` key
- is tried instead. If that doesn't exits either, an empty string is
- returned.
-
- The final value is formatted using `kwargs` (and also `count` if
- available) so the format placeholders must be named instead of
- positional.
-
- If the value isn't a dictionary or a string, is returned as is.
-
- Examples:
-
- >>> translate('hello_world')
- 'hello %(what)s'
- >>> translate('hello_world', what='world')
- 'hello world'
- >>> translate('a_list', what='world')
- ['a', 'b', 'c']
-
- """
- key = str(key)
- locale = locale or self.get_locale()
- value = self.key_lookup(key, locale)
- if not value:
- return Markup('<missing:%s>' % (key, ))
-
- if isinstance(value, dict):
- value = self.pluralize(value, count)
-
- if isinstance(value, basestring):
- kwargs.setdefault('count', count)
- value = value % kwargs
- if key.endswith('_html'):
- return Markup(value)
-
- return value
-
-
- def pluralize(self, d, count):
- """Takes a dictionary and a number and return the value whose key in
- the dictionary is that number. If that key doesn't exist, a `'n'` key
- is tried instead. If that doesn't exits either, an empty string is
- returned. Examples:
-
- >>> i18n = I18n()
- >>> d = {
- 0: u'No apples',
- 1: u'One apple',
- 3: u'Few apples',
- 'n': u'%(count)s apples',
- }
- >>> i18n.pluralize(d, 0)
- 'No apples'
- >>> i18n.pluralize(d, 1)
- 'One apple'
- >>> i18n.pluralize(d, 3)
- 'Few apples'
- >>> i18n.pluralize(d, 10)
- '%(count)s apples'
- >>> i18n.pluralize({0: 'off', 'n': 'on'}, 3)
- 'on'
- >>> i18n.pluralize({0: 'off', 'n': 'on'}, 0)
- 'off'
- >>> i18n.pluralize({}, 3)
- ''
-
- """
- if count is None:
- count = 0
- scount = str(count)
- return d.get(count, d.get(scount, d.get('n', u'')))
-
-
- def to_user_timezone(self, datetime, tzinfo=None):
- """Convert a datetime object to the user's timezone. This
- automatically happens on all date formatting unless rebasing is
- disabled. If you need to convert a `datetime.datetime` object at any
- time to the user's timezone (as returned by `get_timezone` this
- function can be used).
-
- """
- if datetime.tzinfo is None:
- datetime = datetime.replace(tzinfo=UTC)
- tzinfo = tzinfo or self.get_timezone()
- return tzinfo.normalize(datetime.astimezone(tzinfo))
-
-
- def to_utc(self, datetime, tzinfo=None):
- """Convert a datetime object to UTC and drop tzinfo. This is the
- opposite operation to `to_user_timezone`.
-
- """
- if datetime.tzinfo is None:
- tzinfo = tzinfo or self.get_timezone()
- datetime = tzinfo.localize(datetime)
- return datetime.astimezone(UTC).replace(tzinfo=None)
-
-
- def _get_format(self, key, format):
- """A small helper for the datetime formatting functions. Looks up
- format defaults for different kinds.
-
- """
- if format is None:
- format = self.date_formats.get(key)
- if format in ('short', 'medium', 'full', 'long'):
- rv = self.date_formats['%s.%s' % (key, format)]
- if rv is not None:
- format = rv
- return format
-
-
- def _date_format(self, formatter, obj, format, rebase,
- locale=None, tzinfo=None, **extra):
- """Internal helper that formats the date.
-
- """
- locale = locale or self.get_locale()
- extra = {}
- if formatter is not dates.format_date and rebase:
- extra['tzinfo'] = tzinfo or self.get_timezone()
- return formatter(obj, format, locale=locale, **extra)
-
-
- def format(self, value, *args, **kwargs):
- """Return a formatted `value` according to the detected type and
- given parameters. It doesn't know anything about currency, percent or
- scientific formats, so use the other methods for those cases.
-
- """
- locale = kwargs.pop('locale', None)
- tzinfo = kwargs.pop('tzinfo', None)
-
- if isinstance(value, d.date):
- if isinstance(value, d.datetime):
- return self.format_datetime(value, locale=locale,
- tzinfo=tzinfo, *args, **kwargs)
- else:
- return self.format_date(value, locale=locale, tzinfo=tzinfo,
- *args, **kwargs)
-
- if isinstance(value, int):
- return self.format_number(value, locale=locale, *args, **kwargs)
- if isinstance(value, (float, Decimal)):
- return self.format_decimal(value, locale=locale, *args, **kwargs)
-
- if isinstance(value, d.time):
- return self.format_time(value, locale=locale, tzinfo=tzinfo,
- *args, **kwargs)
- if isinstance(value, d.timedelta):
- return self.format_timedelta(value, locale=locale, *args, **kwargs)
-
- return value
-
-
- def format_datetime(self, datetime=None, format=None, rebase=True,
- locale=None, tzinfo=None):
- """Return a date formatted according to the given pattern. If no
- `datetime.datetime` object is passed, the current time is
- assumed. By default rebasing happens which causes the object to
- be converted to the users's timezone (as returned by
- `to_user_timezone`). This function formats both date and
- time.
-
- The format parameter can either be `'short'`, `'medium'`,
- `'long'` or `'full'` (in which cause the language's default for
- that setting is used, or the default from the `Babel.date_formats`
- mapping is used) or a format string as documented by Babel.
-
- This function is also available in the template context as filter
- named `datetimeformat`.
-
- """
- format = self._get_format('datetime', format)
- return self._date_format(dates.format_datetime, datetime, format,
- rebase, locale=locale, tzinfo=tzinfo)
-
-
- def format_date(self, date=None, format=None, rebase=True,
- locale=None, tzinfo=None):
- """Return a date formatted according to the given pattern. If no
- `datetime.datetime` or `datetime.date` object is passed,
- the current time is assumed. By default rebasing happens which causes
- the object to be converted to the users's timezone (as returned by
- `to_user_timezone`). This function only formats the date part
- of a `datetime.datetime` object.
-
- The format parameter can either be `'short'`, `'medium'`,
- `'long'` or `'full'` (in which cause the language's default for
- that setting is used, or the default from the `Babel.date_formats`
- mapping is used) or a format string as documented by Babel.
-
- This function is also available in the template context as filter
- named `dateformat`.
-
- """
- if rebase and isinstance(date, d.datetime):
- date = self.to_user_timezone(date, tzinfo=tzinfo)
- format = self._get_format('date', format)
- return self._date_format(dates.format_date, date, format, rebase,
- locale=locale, tzinfo=tzinfo)
-
-
- def format_time(self, time=None, format=None, rebase=True,
- locale=None, tzinfo=None):
- """Return a time formatted according to the given pattern. If no
- `datetime.datetime` object is passed, the current time is
- assumed. By default rebasing happens which causes the object to
- be converted to the users's timezone (as returned by
- `to_user_timezone`). This function formats both date and
- time.
-
- The format parameter can either be `'short'`, `'medium'`,
- `'long'` or `'full'` (in which cause the language's default for
- that setting is used, or the default from the Babel.date_formats`
- mapping is used) or a format string as documented by Babel.
-
- This function is also available in the template context as filter
- named `timeformat`.
-
- """
- format = self._get_format('time', format)
- return self._date_format(dates.format_time, time, format, rebase,
- locale=locale, tzinfo=tzinfo)
-
-
- def format_timedelta(self, datetime_or_timedelta, granularity='second',
- locale=None):
- """Format the elapsed time from the given date to now or the given
- timedelta.
-
- This function is also available in the template context as filter
- named `timedeltaformat`.
-
- """
- locale = locale or self.get_locale()
- if isinstance(datetime_or_timedelta, d.datetime):
- datetime_or_timedelta = d.datetime.utcnow() - datetime_or_timedelta
- return dates.format_timedelta(datetime_or_timedelta, granularity,
- locale=locale)
-
-
- def format_number(self, number, locale=None):
- """Return the given number formatted for the locale in the
- current request.
-
- number
- : the number to format
-
- return (unicode)
- : the formatted number
-
- This function is also available in the template context as filter
- named `numberformat`.
-
- """
- locale = locale or self.get_locale()
- return numbers.format_number(number, locale=locale)
-
-
- def format_decimal(self, number, format=None, locale=None):
- """Return the given decimal number formatted for the locale in the
- current request.
-
- number
- : the number to format
- format
- : the format to use
-
- return (unicode)
- : the formatted number
-
- This function is also available in the template context as filter
- named `decimalformat`.
-
- """
- locale = locale or self.get_locale()
- return numbers.format_decimal(number, format=format, locale=locale)
-
-
- def format_currency(self, number, currency, format=None, locale=None):
- """Return the given number formatted for the locale in the
- current request.
-
- number
- : the number to format
- currency
- : the currency code
- format
- : the format to use
-
- return (unicode)
- : the formatted number
-
- This function is also available in the template context as filter
- named `currencyformat`.
-
- """
- locale = locale or self.get_locale()
- return numbers.format_currency(
- number, currency, format=format, locale=locale
- )
-
-
- def format_percent(self, number, format=None, locale=None):
- """Return a percent value formatted for the locale in the
- current request.
-
- number
- : the number to format
- format
- : the format to use
-
- return (unicode)
- : the formatted percent number
-
- This function is also available in the template context as filter
- named `percentformat`.
-
- """
- locale = locale or self.get_locale()
- return numbers.format_percent(number, format=format, locale=locale)
-
-
- def format_scientific(self, number, format=None, locale=None):
- """Return value formatted in scientific notation for the locale in
- the current request.
-
- number
- : the number to format
- format
- : the format to use
-
- return (unicode)
- : the formatted percent number
-
- This function is also available in the template context as filter
- named `scientificformat`.
-
- """
- locale = locale or self.get_locale()
- return numbers.format_scientific(number, format=format, locale=locale)
-
View
23 shake/render.py
@@ -59,7 +59,7 @@ class Render(object):
}
- def __init__(self, templates_path=None, loader=None, i18n=None,
+ def __init__(self, templates_path=None, loader=None,
default_mimetype='text/html', response_class=Response, **kwargs):
"""
@@ -70,8 +70,6 @@ def __init__(self, templates_path=None, loader=None, i18n=None,
loader
: optional replacement loader for the templates. If provided,
`templates_path` is ignored.
- i18n
- : optional `I18n` instance for adding internationalization filters.
default_mimetype
: the default MIMETYPE of the response.
response_class
@@ -107,27 +105,8 @@ def __init__(self, templates_path=None, loader=None, i18n=None,
self.env = env
self.default_mimetype = default_mimetype
self.response_class = response_class
-
- if i18n:
- self.init_i18n(i18n)
- def init_i18n(self, i18n):
- self.env.globals['t'] = i18n.translate
- self.env.filters.update({
- 'format': i18n.format,
- 'datetimeformat': i18n.format_datetime,
- 'dateformat': i18n.format_date,
- 'timeformat': i18n.format_time,
- 'timedeltaformat': i18n.format_timedelta,
- 'numberformat': i18n.format_number,
- 'decimalformat': i18n.format_decimal,
- 'currencyformat': i18n.format_currency,
- 'percentformat': i18n.format_percent,
- 'scientificformat': i18n.format_scientific,
- })
-
-
def render(self, tmpl, context=None, to_string=False, **kwargs):
"""Render a template `tmpl` using the given `context`.
If `to_string` is True, the result is returned as is.
View
2  shake/skeletons/project/bundles/common/views.py
@@ -18,7 +18,7 @@ def not_found(request, error):
return app.render('common/error_notfound.html', locals())
-def critical_error(request, error=None):
+def server_error(request, error=None):
return app.render('common/error.html', locals())
View
7 shake/skeletons/project/main.py
@@ -5,7 +5,7 @@
from os.path import join, dirname
import sys
-from moar import Thumbnailer
+# from moar import Thumbnailer
import shake
from shake_files import FileStorage, IMAGES
from solution import SQLAlchemy
@@ -17,7 +17,6 @@
# `import something` to everything inside libs, without install it first.
sys.path.insert(0, join(dirname(__file__), 'libs'))
-
app = shake.Shake(__file__, settings)
# Used for the local development server.
@@ -32,7 +31,7 @@
uploader = FileStorage(app.settings.MEDIA_DIR, app.settings.MEDIA_URL,
allowed=IMAGES)
-thumbnail = Thumbnailer()
+# thumbnail = Thumbnailer()
app.render.env.globals.update({
'STATIC': app.settings.STATIC_URL,
@@ -40,5 +39,5 @@
'SCRIPTS': app.settings.STATIC_URL_SCRIPTS,
'IMAGES': app.settings.STATIC_URL_IMAGES,
'MEDIA': app.settings.MEDIA_URL,
- 'thumbnail': thumbnail,
+ # 'thumbnail': thumbnail,
})
View
2  shake/skeletons/project/requirements.txt
@@ -0,0 +1,2 @@
+# Install all of our production dependencies only.
+-r requirements/prod.txt
View
12 shake/skeletons/project/requirements/common.txt
@@ -0,0 +1,12 @@
+# Common requirements
+# -------------------------------
+# This file collects all required third-party applications that are needed
+# to run this project.
+
+shake
+solution>=1.1.16
+shake-auth>=1.1.7
+shake-files
+mailshake
+# moar
+# pil
View
3  shake/skeletons/project/settings/req-dev.txt → shake/skeletons/project/requirements/dev.txt
@@ -4,5 +4,6 @@
# to run this project while developing, but not in other environments.
# Later you can install all these apps in a row using pip. Example:
#
-# pip install -r req-dev.txt
+# pip install -r requirements/dev.txt
+-r common.txt
View
3  ...e/skeletons/project/settings/req-prod.txt → ...e/skeletons/project/requirements/prod.txt
@@ -4,6 +4,7 @@
# to run this project in production, but not in other environments.
# Later you can install all these apps in a row using pip. Example:
#
-# pip install -r req-prod.txt
+# pip install -r requirements/prod.txt
+-r common.txt
psycopg2
View
9 shake/skeletons/project/requirements/test.txt
@@ -0,0 +1,9 @@
+# Testing requirements
+# -------------------------------
+# This file collects all required third-party applications that are needed
+# to run this project in a staging site, but not in other environments.
+# Later you can install all these apps in a row using pip. Example:
+#
+# pip install -r requirements/test.txt
+
+-r common.txt
View
6 shake/skeletons/project/settings/__init__.py
@@ -8,11 +8,11 @@
if shake.env_is('production'):
- import settings.production as settings
+ import settings.prod as settings
elif shake.env_is('testing'):
- import settings.testing as settings
+ import settings.test as settings
else:
- import settings.development as settings
+ import settings.dev as settings
# # Import local settings
View
2  ...e/skeletons/project/settings/base.py.tmpl → ...skeletons/project/settings/common.py.tmpl
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
- Base settings
+ Common settings
-------------------------------
"""
View
5 ...skeletons/project/settings/development.py → shake/skeletons/project/settings/dev.py
@@ -6,15 +6,12 @@
"""
from mailshake import ToConsoleMailer
-from .base import *
+from .common import *
DEBUG = True
RELOAD = True
-# Optional server name hint
-SERVER_NAME = '127.0.0.1'
-
SQLALCHEMY_URI = 'sqlite:///db.sqlite'
MAILER_CLASS = ToConsoleMailer
View
8 .../skeletons/project/settings/production.py → shake/skeletons/project/settings/prod.py
@@ -6,16 +6,20 @@
"""
from mailshake import SMTPMailer
-from .base import *
+from .common import *
DEBUG = False
RELOAD = False
+# Optional server name hint
+SERVER_NAME = 'example.com'
+DEFAULT_SUBDOMAIN = 'www'
+
SQLALCHEMY_URI = 'postgresql://username:password@127.0.0.1/database'
PAGE_NOT_FOUND = 'bundles.common.views.not_found'
-PAGE_ERROR = 'bundles.common.views.critical_error'
+PAGE_ERROR = 'bundles.common.views.server_error'
PAGE_NOT_ALLOWED = 'bundles.common.views.not_allowed'
MAILER_CLASS = SMTPMailer
View
15 shake/skeletons/project/settings/req.txt
@@ -1,15 +0,0 @@
-# Common requirements
-# -------------------------------
-# This file collects all required third-party applications that are needed
-# to run this project. Later you can install all these apps in a row
-# using pip. Example:
-#
-# pip install -r req.txt
-
-Shake
-Solution>=1.1.16
-Shake-Auth>=1.1.7
-Shake-Files
-MailShake
-Moar
-PIL
View
0  shake/skeletons/project/settings/testing.py → shake/skeletons/project/settings/test.py
File renamed without changes
View
50 shake/wrappers.py
@@ -4,8 +4,6 @@
--------------------------
"""
-from babel import Locale
-from pytz import timezone, UTC
from werkzeug.utils import cached_property
from werkzeug.wrappers import Request as BaseRequest
from werkzeug.wrappers import Response as BaseResponse
@@ -64,11 +62,6 @@ class Request(BaseRequest):
# Set by the application
max_form_memory_size = 0
- # The request locale
- locale = None
-
- # The request timezone (UTC if not defined)
- tzinfo = None
@property
def is_get(self):
@@ -112,49 +105,6 @@ def session(self):
return SecureCookie(secret_key=secret_key)
return SecureCookie.unserialize(data, secret_key)
-
- def get_timezone(self, default=UTC):
- """Returns the timezone that should be used for this request as a
- `DstTzInfo` instance.
-
- Tries the following in order:
- - an attribute called `'tzinfo'`
- - a GET argument called `'tzinfo'`
- - the provided default timezone
-
- """
- tzinfo = self.tzinfo or \
- (self.args and self.args.get('tzinfo')) or \
- default
- if isinstance(tzinfo, basestring):
- tzinfo = timezone(tzinfo)
- self.tzinfo = tzinfo
- return self.tzinfo
-
-
- def get_locale(self, default='en'):
- """Returns the locale that should be used for this request as a
- `babel.Locale` instance.
-
- Tries the following in order:
- - an attribute called `'locale'`
- - a GET argument called `'locale'`
- - the best guess from the ACCEPT_LANGUAGES header
- - the language of the user agent
- - the provided default language
-
- """
- locale = self.locale or \
- (self.args and self.args.get('locale')) or \
- self.accept_languages.best or \
- self.user_agent.language or \
- default
- if isinstance(locale, basestring):
- locale = locale.replace('-', '_')
- locale = Locale.parse(locale, sep='_')
- self.locale = locale
- return self.locale
-
class Response(BaseResponse):
"""The response object that is used by default in shake.
View
7 tests/res/es.yml
@@ -1,7 +0,0 @@
-mytest:
- greeting: Hola
- apple:
- 0: No apples
- 1: One apple
- 3: Few apples
- n: "%(count)s apples"
View
2  tests/res/es_PE.yml
@@ -1,2 +0,0 @@
-mytest:
- greeting: Habla
View
2  tests/res/sub/es.yml
@@ -1,2 +0,0 @@
-mytest:
- greeting: Hola mundo
View
220 tests/test_i18n.py
@@ -1,220 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime as d
-from decimal import Decimal
-from os.path import join, dirname
-
-from babel import Locale
-from babel.dates import format_date, format_datetime, format_time
-from babel.numbers import (format_currency, format_decimal, format_number,
- format_percent, format_scientific)
-from jinja2 import Markup
-from shake import Request, local
-from shake.i18n import I18n
-import pytest
-from pytz import timezone, UTC
-from werkzeug.test import EnvironBuilder
-
-
-def test_search_paths():
- locales_dir = join(dirname(__file__), 'res')
- i18n = I18n(locales_dir)
- assert i18n.search_paths == [locales_dir]
-
-
-def test_app_defaults():
- class FakeSettings(object):
- DEFAULT_LOCALE = 'es_PE'
- DEFAULT_TIMEZONE = 'America/Lima'
-
- class FakeApp(object):
- settings = FakeSettings()
-
- i18n = I18n(app=FakeApp())
-
- assert Locale('es', 'PE') == i18n.get_locale()
- assert timezone('America/Lima') == i18n.get_timezone()
-
-
-def test_request_settings():
- class FakeSettings(object):
- DEFAULT_LOCALE = 'es_PE'
- DEFAULT_TIMEZONE = 'America/Lima'
-
- class FakeApp(object):
- settings = FakeSettings()
-
- i18n = I18n(app=FakeApp())
- request = get_test_request()
- request.locale = 'en'
- request.tzinfo = 'US/Eastern'
- local.request = request
-
- assert Locale.parse('en') == i18n.get_locale()
- assert timezone('US/Eastern') == i18n.get_timezone()
-
-
-def test_load_language():
- class FakeSettings(object):
- DEFAULT_LOCALE = 'en'
-
- class FakeApp(object):
- settings = FakeSettings()
-
- locales_dir = join(dirname(__file__), 'res')
- i18n = I18n(app=FakeApp())
- locale = Locale('es')
-
- data = i18n.load_language(locales_dir, Locale('es'))
- assert data['mytest']['greeting'] == u'Hola'
- data = i18n.load_language(locales_dir, Locale('es', 'PE'))
- assert data['mytest']['greeting'] == u'Habla'
- data = i18n.load_language(locales_dir, Locale('es', 'CO'))
- assert data['mytest']['greeting'] == u'Hola'
- data = i18n.load_language(locales_dir, Locale('en'))
- assert data is None
-
-
-def test_find_keypath():
- locales_dir = join(dirname(__file__), 'res')
- i18n = I18n(locales_dir)
-
- path, subkey = i18n.find_keypath('mytest.greeting')
- assert path == locales_dir
- assert subkey == 'mytest.greeting'
-
- path, subkey = i18n.find_keypath('sub:mytest.greeting')
- assert path == join(locales_dir, 'sub')
- assert subkey == 'mytest.greeting'
-
-
-def test_key_lookup():
- class FakeSettings(object):
- DEFAULT_LOCALE = 'en'
-
- class FakeApp(object):
- settings = FakeSettings()
-
- locales_dir = join(dirname(__file__), 'res')
- i18n = I18n(locales_dir, app=FakeApp())
- locale = Locale('es')
-
- assert i18n.key_lookup('mytest.greeting', locale) == u'Hola'
- assert i18n.key_lookup('mytest.bla', locale) == None
- assert i18n.key_lookup('sub:mytest.greeting', locale) == u'Hola mundo'
-
-
-def test_translate():
- class FakeSettings(object):
- DEFAULT_LOCALE = 'es_PE'
-
- class FakeApp(object):
- settings = FakeSettings()
-
- locales_dir = join(dirname(__file__), 'res')
- i18n = I18n(locales_dir, app=FakeApp())
- locale = Locale('es')
-
- assert i18n.translate('mytest.greeting', locale=locale) == u'Hola'
- assert i18n.translate('mytest.greeting') == u'Habla'
- assert i18n.translate('mytest.apple', 3, locale=locale) == u'Few apples'
- assert i18n.translate('mytest.apple', 10, locale=locale) == u'10 apples'
- assert i18n.translate('bla', locale=locale) == Markup(u'<missing:bla>')
-
-
-def test_pluralize():
- i18n = I18n()
- d = {
- 0: u'No apples',
- 1: u'One apple',
- 3: u'Few apples',
- 'n': u'%(count)s apples',
- }
- assert i18n.pluralize(d, 0) == u'No apples'
- assert i18n.pluralize(d, 1) == u'One apple'
- assert i18n.pluralize(d, 3) == u'Few apples'
- assert i18n.pluralize(d, 10) == u'%(count)s apples'
-
- d = {
- 0: u'off',
- 'n': u'on'
- }
- assert i18n.pluralize(d, 3) == u'on'
-
- d = {
- 0: u'off',
- 'n': u'on'
- }
- assert i18n.pluralize(d, 0) == u'off'
-
- assert i18n.pluralize({}, 3) == u''
-
-
-def test_to_user_timezone():
- i18n = I18n()
- tzinfo = timezone('US/Eastern')
- now = d.datetime.utcnow()
- result = i18n.to_user_timezone(now, tzinfo=tzinfo)
- expected = tzinfo.fromutc(now)
- assert result == expected
-
-
-def test_to_utc():
- i18n = I18n()
- tzinfo = timezone('US/Eastern')
- now = d.datetime.utcnow()
- tznow = tzinfo.fromutc(now)
- assert i18n.to_utc(tznow) == now
-
-
-def test_format():
- i18n = I18n()
- locale = 'en_US'
- tzinfo = UTC
-
- test_cases = [
- (456, format_number),
- (3.14159, format_decimal),
- (Decimal(3.14159), format_decimal),
- ]
- for value, bf in test_cases:
- assert i18n.format(value, locale=locale) == bf(value, locale=locale)
-
- test_cases = [
- (d.datetime(2012, 7, 28, 3, 4, 5), format_datetime),
- (d.date.today(), format_date),
- (d.time(3, 4, 5), format_time),
- ]
- for value, bf in test_cases:
- result = i18n.format(value, locale=locale, tzinfo=tzinfo)
- expected = bf(value, locale=locale)
- assert result == expected
-
-
-def test_more_formatters():
- i18n = I18n()
- locale = 'en_US'
- v = 231.456
-
- result = i18n.format_currency(v, 'USD', locale=locale)
- expected = format_currency(v, 'USD', locale=locale)
- assert result == expected
-
- result = i18n.format_percent(v, locale=locale)
- expected = format_percent(v, locale=locale)
- assert result == expected
-
- result = i18n.format_scientific(v, locale=locale)
- expected = format_scientific(v, locale=locale)
- assert result == expected
-
-
-# -----------------------------------------------------------------------------
-
-def get_test_env(path, **kwargs):
- builder = EnvironBuilder(path=path, **kwargs)
- return builder.get_environ()
-
-
-def get_test_request(path='/', **kwargs):
- env = get_test_env(path, **kwargs)
- return Request(env)
Please sign in to comment.
Something went wrong with that request. Please try again.