~sirn/fanboi2

ref: 8b56ac65160b4b491c3a1bf0864cb47f9e996cd1 fanboi2/fanboi2/__init__.py -rw-r--r-- 4.1 KiB
8b56ac65Kridsada Thanabulpong Fix broken error report due to Celery no longer raise original exceptions. 7 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import hashlib
import pyramid_jinja2
from functools import lru_cache
from IPy import IP
from pyramid.config import Configurator
from pyramid.exceptions import NotFound
from pyramid.path import AssetResolver
from pyramid.view import append_slash_notfound_view
from pyramid_beaker import session_factory_from_settings
from sqlalchemy import engine_from_config
from .cache import cache_region, Jinja2CacheExtension
from .formatters import *
from .models import DBSession, Base, redis_conn, identity
from .utils import akismet, dnsbl


def remote_addr(request):
    """Similar to built-in ``request.remote_addr`` but will fallback to
    ``HTTP_X_FORWARDED_FOR`` if defined and ``REMOTE_ADDR`` is private or
    loopback address.
    """
    ipaddr = IP(request.environ.get('REMOTE_ADDR', '255.255.255.255'))
    if ipaddr.iptype() == "PRIVATE":
        return request.environ.get('HTTP_X_FORWARDED_FOR', str(ipaddr))
    return str(ipaddr)


def route_name(request):
    """Returns :attr:`name` of current :attr:`request.matched_route`."""
    return request.matched_route.name


@lru_cache(maxsize=10)
def _get_asset_hash(path):
    if ':' in path:
        package, path = path.split(':')
        resolver = AssetResolver(package)
    else:
        resolver = AssetResolver()
    fullpath = resolver.resolve(path).abspath()
    md5 = hashlib.md5()
    with open(fullpath, 'rb') as f:
        for chunk in iter(lambda: f.read(128 * md5.block_size), b''):
            md5.update(chunk)
    return md5.hexdigest()


def tagged_static_path(request, path, **kwargs):
    """Similar to built-in :meth:`request.static_path` but append first 8
    characters of file hash as query string ``h`` to it forcing proxy server
    and browsers to expire cache immediately after the file is modified.
    """
    kwargs['_query'] = {'h': _get_asset_hash(path)[:8]}
    return request.static_path(path, **kwargs)


def configure_components(cfg):  # pragma: no cover
    """Configure the application components e.g. database connection."""
    # BUG: configure_components should be called after pyramid.Configurator
    # in order to prevent an importlib bug to cause pkg_resources to fail.
    # Tasks are imported here because of the same reason (Celery uses
    # importlib internally.)
    #
    # This bug only applies to Python 3.2.3 only.
    from .tasks import celery, configure_celery
    engine = engine_from_config(cfg, 'sqlalchemy.', client_encoding='utf8')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    redis_conn.from_url(cfg['redis.url'])
    celery.config_from_object(configure_celery(cfg))
    identity.configure_tz(cfg['app.timezone'])
    akismet.configure_key(cfg['akismet.key'])
    dnsbl.configure_providers(cfg['dnsbl.providers'])
    cache_region.configure_from_config(cfg, 'dogpile.')
    cache_region.invalidate()


def main(global_config, **settings):  # pragma: no cover
    """This function returns a Pyramid WSGI application."""
    session_factory = session_factory_from_settings(settings)
    config = Configurator(settings=settings)
    configure_components(settings)

    config.set_session_factory(session_factory)
    config.set_request_property(remote_addr)
    config.set_request_property(route_name)
    config.add_request_method(tagged_static_path)

    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('root', '/')
    config.add_route('board', '/{board:\w+}/')
    config.add_route('board_all', '/{board:\w+}/all/')
    config.add_route('board_new', '/{board:\w+}/new/')
    config.add_route('topic', '/{board:\w+}/{topic:\d+}/')
    config.add_route('topic_scoped', '/{board:\w+}/{topic:\d+}/{query}/')
    config.add_view(append_slash_notfound_view, context=NotFound)

    config.include(pyramid_jinja2)
    config.add_jinja2_extension(Jinja2CacheExtension)
    jinja2_env = config.get_jinja2_environment()
    jinja2_env.cache_region = cache_region

    jinja2_env.filters['datetime'] = format_datetime
    jinja2_env.filters['formatpost'] = format_post
    jinja2_env.filters['isotime'] = format_isotime
    jinja2_env.filters['markdown'] = format_markdown
    jinja2_env.filters['markup'] = format_text
    config.scan()

    return config.make_wsgi_app()