如何在金字塔应用程序启动时获取Registry().settings?
我习惯在 Django 和 Gunicorn 上开发 Web 应用程序。
对于 Django,Django 应用程序中的任何应用程序模块都可以通过 django.conf.settings 获取部署设置。 “settings.py”是用Python编写的,因此可以动态定义任意设置和预处理。
对于gunicorn,它具有三个按优先顺序排列的配置位置,并且一个设置注册表类实例将这些配置组合在一起。(但通常这些设置仅用于gunicorn,而不是应用程序。)
- 命令行参数。
- 配置文件。 (就像 Django 一样,写成 Python 可以有任意的 动态设置。)
- 粘贴应用程序设置。
对于 Pyramid,根据 Pyramid 文档,部署设置通常可以放入pyramid.registry.Registry().settings 中。但似乎只有当 Pyramid.router.Router() 实例存在时才能访问它。 也就是说,在应用程序“main.py”的启动过程中,pyramid.threadlocal.get_current_registry().settings 返回 None。
例如,我通常在SQLAlchemy模型模块中定义一些业务逻辑,这需要如下的部署设置。
myapp/models.py
from sqlalchemy import Table, Column, Types
from sqlalchemy.orm import mapper
from pyramid.threadlocal import get_current_registry
from myapp.db import session, metadata
settings = get_current_registry().settings
mytable = Table('mytable', metadata,
Column('id', Types.INTEGER, primary_key=True,)
(other columns)...
)
class MyModel(object):
query = session.query_property()
external_api_endpoint = settings['external_api_uri']
timezone = settings['timezone']
def get_api_result(self):
(interact with external api ...)
mapper(MyModel, mytable)
但是,“settings['external_api_endpoint']”会引发 TypeError 异常,因为“settings”为 None。
我想到了两种解决方案。
定义一个可调用函数,它接受“models.py”中的“config”参数,并且“main.py”用 Configurator() 实例。
myapp/models.py
from sqlalchemy 导入表、列、类型 从 sqlalchemy.orm 导入映射器 从 myapp.db 导入会话、元数据 _g = 全局变量() def 初始化(配置): 设置 = config.get_settings() mytable = 表('mytable', 元数据, 列('id',Types.INTEGER,rimary_key = True,) (其他栏目...) ) 类 MyModel(对象): 查询 = session.query_property() external_api_endpoint = 设置['external_api_endpoint'] def get_api_result(self): (与外部API交互)... 映射器(MyModel,mytable) _g['我的模型'] = 我的模型 _g['mytable'] = mytable
或者,放置一个空模块“app/settings.py”,稍后再将设置放入其中。
myapp/__init__.py
从pyramid.config导入配置器 从 .resources 导入 RootResource def main(全局配置,**设置): 配置=配置器( 设置=设置, root_factory = 根资源, ) 导入 myapp.settings myapp.setting.settings = config.get_settings() (其他配置...) 返回 config.make_wsgi_app()
和其他方案都满足要求,但是我觉得麻烦。我想要的是以下内容。
开发.ini
定义粗略设置,因为development.ini只能有字符串类型常量。
<前><代码>[应用程序:myapp] 使用=鸡蛋:myapp 环境 = dev0 api_签名 = xxxxxxmyapp/settings.py
基于development.ini定义详细设置,因为可以设置任意变量(类型)。
导入日期时间、urllib 从 pytz 导入时区 从pyramid.threadlocal导入get_current_registry Pyramid_settings = get_current_registry().settings 如果pyramid_settings['env'] == '生产': api_endpoint_uri = 'http://api.external.com/?{0}' 时区 = 时区('美国/东部') elif Pyramid_settings['env'] == 'dev0': api_endpoint_uri = 'http://sandbox0.external.com/?{0}' timezone = timezone('澳大利亚/悉尼') elif Pyramid_settings['env'] == 'dev1': api_endpoint_uri = 'http://sandbox1.external.com/?{0}' 时区 = 时区('JP/东京') api_endpoint_uri = api_endpoint_uri.format(urllib.urlencode({'signature':pyramid_settings['api_signature']}))
然后,其他模块可以通过“import myapp.settings”获取任意部署设置。 或者,如果Registry().settings比“settings.py”更好,则**settings kwargs和“settings.py”可以在“main.py”启动过程中组合并注册到Registry().settings中。
无论如何,如何在启动时获取设置字典?或者,Pyramid 温和地迫使我们将需要部署设置的每个代码放入“视图”可调用对象中,这些可调用对象可以随时通过 request.registry.settings 获取设置字典?
编辑
谢谢,迈克尔和克里斯。
我终于明白为什么 Pyramid 使用线程本地变量(注册表和请求),特别是多个 Pyramid 应用程序的注册表对象。
然而,在我看来,部署设置通常会影响可能定义特定于应用程序的内容的业务逻辑。这些逻辑通常放在一个或多个 Python 模块中,这些模块可能不是“app/init.py”或“app/views.py”,可以轻松访问 Config() 或Registry( )。这些 Python 模块通常在 Python 进程级别是“全局”的。
也就是说,即使多个 Pyramid 应用程序共存,尽管它们有自己的线程局部变量,它们也必须共享那些可能包含 Python 进程级别的应用程序特定内容的“全局”Python 模块。
当然,每个模块都可以有“initialize()”调用,它由应用程序“main”可调用的 Configurator() 调用,或者通过一系列长的函数调用传递 Registory() 或 Request() 对象可以满足通常的要求要求。但是,我想金字塔初学者(像我一样)或拥有“大型应用程序或如此多设置”的开发人员可能会感到麻烦,尽管这是金字塔设计。
所以,我认为,Registry().settings 应该只有真正的“线程局部”变量,而不应该有正常的业务逻辑设置。开发人员应负责隔离多个特定于应用程序的模块、类、可调用变量等。 到目前为止,从我的角度来看,我会接受克里斯的回答。或者在“main”可调用中,执行“execfile('settings.py', settings, settings)”并将其放在某个“全局”空间中。
I am used to develop web applications on Django and gunicorn.
In case of Django, any application modules in a Django application can get deployment settings through django.conf.settings. The "settings.py" is written in Python, so that any arbitrary settings and pre-processing can be defined dynamically.
In case of gunicorn, it has three configuration places in order of precedence, and one settings registry class instance combines those.(But usually these settings are used only for gunicorn not application.)
- Command line parameters.
- Configuration file. (like Django, written in
Python which can have any arbitrary
settings dynamically.) - Paster application settings.
In case of Pyramid, according to Pyramid documentation, deployment settings may be usually put into pyramid.registry.Registry().settings. But it seems to be accessed only when a pyramid.router.Router() instances exists.
That is pyramid.threadlocal.get_current_registry().settings returns None, during the startup process in an application "main.py".
For example, I usually define some business logic in SQLAlchemy model modules, which requires deployment settings as follows.
myapp/models.py
from sqlalchemy import Table, Column, Types
from sqlalchemy.orm import mapper
from pyramid.threadlocal import get_current_registry
from myapp.db import session, metadata
settings = get_current_registry().settings
mytable = Table('mytable', metadata,
Column('id', Types.INTEGER, primary_key=True,)
(other columns)...
)
class MyModel(object):
query = session.query_property()
external_api_endpoint = settings['external_api_uri']
timezone = settings['timezone']
def get_api_result(self):
(interact with external api ...)
mapper(MyModel, mytable)
But, "settings['external_api_endpoint']" raises a TypeError exception because the "settings" is None.
I thought two solutions.
Define a callable which accepts "config" argument in "models.py" and "main.py" calls it with a
Configurator() instance.myapp/models.py
from sqlalchemy import Table, Column, Types from sqlalchemy.orm import mapper from myapp.db import session, metadata _g = globals() def initialize(config): settings = config.get_settings() mytable = Table('mytable', metadata, Column('id', Types.INTEGER, rimary_key = True,) (other columns ...) ) class MyModel(object): query = session.query_property() external_api_endpoint = settings['external_api_endpoint'] def get_api_result(self): (interact with external api)... mapper(MyModel, mytable) _g['MyModel'] = MyModel _g['mytable'] = mytable
Or, put an empty module "app/settings.py", and put setting into it later.
myapp/__init__.py
from pyramid.config import Configurator from .resources import RootResource def main(global_config, **settings): config = Configurator( settings = settings, root_factory = RootResource, ) import myapp.settings myapp.setting.settings = config.get_settings() (other configurations ...) return config.make_wsgi_app()
Both and other solutions meet the requirements, but I feel troublesome. What I want is the followings.
development.ini
defines rough settings because development.ini can have only string type constants.
[app:myapp] use = egg:myapp env = dev0 api_signature = xxxxxx
myapp/settings.py
defines detail settings based on development.ini, beacause any arbitrary variables(types) can be set.
import datetime, urllib from pytz import timezone from pyramid.threadlocal import get_current_registry pyramid_settings = get_current_registry().settings if pyramid_settings['env'] == 'production': api_endpoint_uri = 'http://api.external.com/?{0}' timezone = timezone('US/Eastern') elif pyramid_settings['env'] == 'dev0': api_endpoint_uri = 'http://sandbox0.external.com/?{0}' timezone = timezone('Australia/Sydney') elif pyramid_settings['env'] == 'dev1': api_endpoint_uri = 'http://sandbox1.external.com/?{0}' timezone = timezone('JP/Tokyo') api_endpoint_uri = api_endpoint_uri.format(urllib.urlencode({'signature':pyramid_settings['api_signature']}))
Then, other modules can get arbitrary deployment settings through "import myapp.settings".
Or, if Registry().settings is preferable than "settings.py", **settings kwargs and "settings.py" may be combined and registered into Registry().settings during "main.py" startup process.
Anyway, how to get the settings dictionay during startup time ? Or, Pyramid gently forces us to put every code which requires deployment settings in "views" callables which can get settings dictionary anytime through request.registry.settings ?
EDIT
Thanks, Michael and Chris.
I at last understand why Pyramid uses threadlocal variables(registry and request), in particular registry object for more than one Pyramid applications.
In my opinion, however, deployment settings usually affect business logics that may define application-specific somethings. Those logics are usually put in one or more Python modules that may be other than "app/init.py" or "app/views.py" that can easily get access to Config() or Registry(). Those Python modules are normally "global" at Python process level.
That is, even when more than one Pyramid applications coexist, despite their own threadlocal variables, they have to share those "global" Python modules that may contain applicatin-specific somethings at Python process level.
Of cause, every those modules can have "initialize()" callalbe which is called with a Configurator() by the application "main" callable, or passing Registory() or Request() object through so long series of function calls can meet usual requirements. But, I guess Pyramid beginers (like me) or developers who has "large application or so many settings" may feel troublesome, although that is Pyramid design.
So, I think, Registry().settings should have only real "thread-local" variables, and should not have normal business-logic settings. Responsibility for segregation of multiple application-specific module, classes, callables variables etc. should be taken by developer.
As of now, from my viewpoint, I will take Chris's answer. Or in "main" callable, do "execfile('settings.py', settings, settings)" and put it in some "global" space.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
另一种选择是,如果您喜欢通过 Python 进行全局配置,请创建一个 settings.py 文件。如果它需要 ini 文件中的值,则解析 ini 文件并将它们取出(在模块范围内,因此它在导入时运行):
'config:development.ini' 是 ini 文件的名称(前缀为 'config: ')。 'myapp' 是配置文件中代表您的应用程序的部分名称(例如 [app:myapp])。 “relative_to”是可以在其中找到配置文件的目录名称。
Another option, if you enjoy global configuration via Python, create a settings.py file. If it needs values from the ini file, parse the ini file and grab them out (at module scope, so it runs at import time):
'config:development.ini' is the name of the ini file (prefixed with 'config:'). 'myapp' is the section name in the config file representing your app (e.g. [app:myapp]). "relative_to" is the directory name in which the config file can be found.
我使用的模式是将配置器传递给需要初始化的模块。 Pyramid 不使用任何全局变量,因为设计目标是能够在同一进程中运行 Pyramid 的多个实例。线程局部变量是全局的,但它们对于当前请求来说是本地的,因此不同的 Pyramid 应用程序可以从不同的线程同时推送到它们。
考虑到这一点,如果您确实想要一个全局设置字典,则必须自己处理。您甚至可以通过调用 config.begin() 自行将注册表推送到线程本地管理器上。
我认为这里要注意的主要事情是您不应该在模块级别调用 get_current_registry() ,因为在导入时您并不能真正保证线程局部变量已初始化,但是,在您的
init_model()
函数中,如果您调用get_current_registry()
,那么如果您之前调用过config.begin()
就可以了。抱歉,这有点复杂,但这是一个常见问题,最好的答案是:将配置器传递给需要它的子模块,并允许它们向注册表/设置对象添加内容以供以后使用。
The pattern that I use is to pass the
Configurator
to modules that need to be initialized. Pyramid doesn't use any global variables because a design goal is to be able to run multiple instances of Pyramid in the same process. The threadlocals are global, but they are local to the current request, so different Pyramid apps can push to them at the same time from different threads.With this in mind, if you do want a global settings dictionary you'll have to take care of that yourself. You could even push the registry onto the threadlocal manager yourself by calling
config.begin()
.I think the major thing to take away here is that you shouldn't be calling
get_current_registry()
at the module level, because at the time of import you aren't really guaranteed that the threadlocals are initialized, however in yourinit_model()
function if you callget_current_registry()
, you'd be fine if you previously calledconfig.begin()
.Sorry this is a little convoluted, but it's a common question and the best answer is: pass the configurator to your submodules that need it and allow them to add stuff to the registry/settings objects for use later.
与 Django 不同,Pyramid 使用 PasteDeploy 的静态配置。
你的[编辑]部分是一个很好的解决方案,我认为金字塔社区应该考虑这种用法。
Pyramid uses static configration by PasteDeploy, unlike Django.
Your [EDIT] part is a nice solution, I think Pyramid community should consider such usage.