From 060660c05c5936b8b17002cfa208f9997b598b55 Mon Sep 17 00:00:00 2001 From: tonyseek Date: Tue, 19 Jun 2012 04:33:13 +0800 Subject: [PATCH 1/3] created the app class. --- feedbundle/app.cfg | 0 feedbundle/app.py | 89 ++++++++++++++++++++++++++++++++++ feedbundle/corelib/__init__.py | 0 feedbundle/corelib/logging.py | 31 ++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 feedbundle/app.cfg create mode 100644 feedbundle/app.py create mode 100644 feedbundle/corelib/__init__.py create mode 100644 feedbundle/corelib/logging.py diff --git a/feedbundle/app.cfg b/feedbundle/app.cfg new file mode 100644 index 0000000..e69de29 diff --git a/feedbundle/app.py b/feedbundle/app.py new file mode 100644 index 0000000..4f1916b --- /dev/null +++ b/feedbundle/app.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- + +import os + +from flask import Flask +from werkzeug import import_string + +from feedbundle.corelib.logging import make_file_handler + + +class FeedBundle(Flask): + """The web application.""" + + CONFIG_ENV = "FEEDBUNDLE_CONFIG" + BUILTIN_CONFIG = "app.cfg" + + def __init__(self, import_name=__package__, *args, **kwargs): + super(FeedBundle, self).__init__(import_name, *args, **kwargs) + + #: load built-in and local configuration + self.config.from_pyfile(self.get_absolute_path(self.BUILTIN_CONFIG)) + self.config.from_envvar(self.CONFIG_ENV) + self.config.setdefault("BUILTIN_BLUEPRINTS", []) + self.config.setdefault("BUILTIN_EXTENSIONS", []) + self.config.setdefault("BLUEPRINTS", []) + self.config.setdefault("EXTENSIONS", []) + self.config.setdefault("LOGGING_FILE", None) + + #: initialize the application + self.init_extensions() + self.init_logger() + self.init_blueprints() + + def init_extensions(self): + """Initialize the extensions of the application.""" + #: make a set to contain names of the extensions + extensions = set(self.config['BUILTIN_EXTENSIONS']) + extensions.update(self.config['EXTENSIONS']) + + #: import and install the extensions + for extension_name in extensions: + extension = import_string(extension_name) + extension.init_app(self) + + def init_logger(self): + """Append more handlers to the application logger.""" + #: create a file handler and install it + filepath = self.config['LOGGING_FILE'] + if filepath: + file_handler = make_file_handler(filepath) + self.logger.addHandler(file_handler) + + def init_blueprints(self): + """Register all blueprints to the application.""" + #: make a set to contain names of the blueprints + blueprints = set(self.config['BUILTIN_BLUEPRINTS']) + blueprints.update(self.config['BLUEPRINTS']) + + #: import and install all blueprints + for blueprint_name in blueprints: + package_name = blueprint_name.replace(":", ".").rsplit(".", 1) + self.register_blueprint_by_name(blueprint_name, package_name) + + def register_blueprint_by_name(self, name, package_name): + """Register the blueprint by its name.""" + #: import the blueprint object and its package + blueprint = import_string(name) + package = import_string(package_name) + + #: load the all submodules + for module_name in getattr(package, "__all__", []): + import_string("%s.%s" % (package_name, module_name), silent=True) + + #: register the blueprint + self.register_blueprint(blueprint) + self.logger.info("Loaded %r" % name) + + def get_absolute_path(self, relative_path): + """Create a absolute path from a relative path. + + Example: + >>> app.root_path + '/home/tonyseek/projects/myapp/myapp' + >>> app.get_full_path("../somefile") + '/home/tonyseek/projects/myapp/somefile' + """ + path = os.path.join(self.root_path, relative_path) + return os.path.abspath(path) diff --git a/feedbundle/corelib/__init__.py b/feedbundle/corelib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/feedbundle/corelib/logging.py b/feedbundle/corelib/logging.py new file mode 100644 index 0000000..54f557d --- /dev/null +++ b/feedbundle/corelib/logging.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- + +from __future__ import absolute_import + +import logging +import logging.handlers + + +def make_multiline_formatter(): + """Create a logging formatter to dump all information.""" + formatter = ["-" * 78, + "Name: %(name)s", + "Message type: %(levelname)s", + "Location: %(pathname)s:%(lineno)d", + "Module: %(module)s", + "Function: %(funcName)s", + "Time: %(asctime)s", + "Message:", + "%(message)s", + "-" * 78] + return logging.Formatter("\n".join(formatter)) + + +def make_file_handler(path, formatter_factory=make_multiline_formatter, + level=logging.DEBUG): + """Create a file handler to record logging information into a file.""" + file_handler = logging.handlers.WatchedFileHandler(path) + file_handler.setLevel(level) + file_handler.setFormatter(formatter_factory()) + return file_handler From 87042b8527c637b93e71ee1bb1ccf3469c9c163a Mon Sep 17 00:00:00 2001 From: tonyseek Date: Tue, 19 Jun 2012 18:04:25 +0800 Subject: [PATCH 2/3] created the smtp logging handler and script manager. --- .gitignore | 2 ++ Makefile | 10 +++++----- feedbundle/app.py | 19 +++++++++++++++++-- feedbundle/corelib/logging.py | 19 +++++++++++++++---- manage.py | 22 ++++++++++++++++++++++ 5 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 manage.py diff --git a/.gitignore b/.gitignore index 83e0c3f..2351684 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ *.pyc *.pyo *.egg-info +*.log +*.dev.cfg __pycache__ bin build diff --git a/Makefile b/Makefile index ce12eac..4ec6652 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,13 @@ clean-build: rm -fr build/ rm -fr dist/ rm -fr *.egg-info - + find . -name '*.log' -exec rm -fr {} \; clean-pyc: - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - find . -name '__pycache__' -exec rm -fr {} + + find . -name '*.pyc' -exec rm -f {} \; + find . -name '*.pyo' -exec rm -f {} \; + find . -name '*~' -exec rm -f {} \; + find . -name '__pycache__' -exec rm -fr {} \; docs: $(MAKE) -C docs html diff --git a/feedbundle/app.py b/feedbundle/app.py index 4f1916b..fdea9e6 100644 --- a/feedbundle/app.py +++ b/feedbundle/app.py @@ -3,10 +3,10 @@ import os -from flask import Flask +from flask import Flask, request from werkzeug import import_string -from feedbundle.corelib.logging import make_file_handler +from feedbundle.corelib.logging import make_file_handler, make_smtp_handler class FeedBundle(Flask): @@ -26,6 +26,7 @@ def __init__(self, import_name=__package__, *args, **kwargs): self.config.setdefault("BLUEPRINTS", []) self.config.setdefault("EXTENSIONS", []) self.config.setdefault("LOGGING_FILE", None) + self.config.setdefault("LOGGING_EMAILS", []) #: initialize the application self.init_extensions() @@ -51,6 +52,20 @@ def init_logger(self): file_handler = make_file_handler(filepath) self.logger.addHandler(file_handler) + #: create a email handler and install it + emails = self.config['LOGGING_EMAILS'] + smtp_handler_installed = [False] + + @self.before_first_request + def register_smtp_handler(): + #: defer initialize because of the 'request.host' + if emails and not smtp_handler_installed[0]: + fromaddr = "applog@%s" % request.host + subject = "FeedBundle Application Log" + smtp_handler = make_smtp_handler(fromaddr, emails, subject) + self.logger.addHandler(smtp_handler) + smtp_handler_installed[0] = True + def init_blueprints(self): """Register all blueprints to the application.""" #: make a set to contain names of the blueprints diff --git a/feedbundle/corelib/logging.py b/feedbundle/corelib/logging.py index 54f557d..9f21c59 100644 --- a/feedbundle/corelib/logging.py +++ b/feedbundle/corelib/logging.py @@ -25,7 +25,18 @@ def make_multiline_formatter(): def make_file_handler(path, formatter_factory=make_multiline_formatter, level=logging.DEBUG): """Create a file handler to record logging information into a file.""" - file_handler = logging.handlers.WatchedFileHandler(path) - file_handler.setLevel(level) - file_handler.setFormatter(formatter_factory()) - return file_handler + handler = logging.handlers.WatchedFileHandler(path) + handler.setLevel(level) + handler.setFormatter(formatter_factory()) + return handler + + +def make_smtp_handler(fromaddr, toaddrs, host="127.0.0.1", + subject="Application Log", + formatter_factory=make_multiline_formatter, + level=logging.ERROR): + """Create a STMP handler to send logging information with email.""" + handler = logging.handlers.SMTPHandler(host, fromaddr, toaddrs, subject) + handler.setLevel(level) + handler.setFormatter(formatter_factory()) + return handler diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..979a390 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- + +import os + +from flask.ext.script import Manager + +from feedbundle.app import FeedBundle + + +#: be more convenient for developer, +#: automatic to set configuration environment variable. +if os.path.exists("feedbundle.dev.cfg"): + os.environ['FEEDBUNDLE_CONFIG'] = os.path.abspath("feedbundle.dev.cfg") + + +app = FeedBundle() +manager = Manager(app) + + +if __name__ == "__main__": + manager.run() From 8a6ad160e0c434125ad5dd1be177705a37bc8af1 Mon Sep 17 00:00:00 2001 From: hbc Date: Thu, 5 Jul 2012 14:27:30 +0800 Subject: [PATCH 3/3] bugfix: fixed init_blueprints package_name --- feedbundle/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feedbundle/app.py b/feedbundle/app.py index fdea9e6..aa29ba0 100644 --- a/feedbundle/app.py +++ b/feedbundle/app.py @@ -74,7 +74,7 @@ def init_blueprints(self): #: import and install all blueprints for blueprint_name in blueprints: - package_name = blueprint_name.replace(":", ".").rsplit(".", 1) + package_name = blueprint_name.replace(":", ".").rsplit(".", 1)[0] self.register_blueprint_by_name(blueprint_name, package_name) def register_blueprint_by_name(self, name, package_name):