Как да получите Registry().settings по време на стартиране на приложението Pyramid?

Свикнал съм да разработвам уеб приложения на Django и gunicorn.

В случай на Django, всички модули на приложение в приложение на Django могат да получат настройки за внедряване чрез django.conf.settings. „settings.py“ е написан на Python, така че всякакви произволни настройки и предварителна обработка могат да бъдат дефинирани динамично.

В случай на gunicorn, той има три конфигурационни места по реда на приоритет и един екземпляр на регистър на класа на настройките ги комбинира. (Но обикновено тези настройки се използват само за gunicorn, а не за приложение.)

  1. Параметри на командния ред.
  2. Конфигурационен файл. (като Django, написан на Python, който може да има всякакви произволни настройки динамично.)
  3. Настройки на приложението Paster.

В случай на Pyramid, според документацията на Pyramid, настройките за внедряване обикновено могат да бъдат поставени в pyramid.registry.Registry().settings. Но изглежда, че е достъпен само когато съществува екземпляр на pyramid.router.Router(). Това е pyramid.threadlocal.get_current_registry().settings връща None по време на процеса на стартиране в приложение "main.py".

Например, обикновено дефинирам някаква бизнес логика в модулите на модела 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.

Мислех си за две решения.

  • Дефинирайте извикваем, който приема аргумент "config" в "models.py" и "main.py" го извиква с екземпляр на Configurator().

    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
    
  • Или поставете празен модул "app/settings.py" и поставете настройка в него по-късно.

    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()
    

И двете, и други решения отговарят на изискванията, но се чувствам обезпокоителен. Това, което искам е следното.

  • развитие.ini

    дефинира груби настройки, тъй като development.ini може да има само константи от типа низ.

    [app:myapp]
    use = egg:myapp
    env = dev0
    api_signature = xxxxxx
    
  • myapp/settings.py

    дефинира подробни настройки въз основа на development.ini, тъй като могат да се задават всякакви произволни променливи (типове).

    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']}))
    

След това други модули могат да получат произволни настройки за внедряване чрез „import myapp.settings“. Или, ако Registry().settings е за предпочитане от "settings.py", **settings kwargs и "settings.py" могат да бъдат комбинирани и регистрирани в Registry().settings по време на процеса на стартиране на "main.py".

Както и да е, как да получа речника на настройките по време на стартиране? Или Pyramid внимателно ни принуждава да поставим всеки код, който изисква настройки за разгръщане, в извиквания на "views", които могат да получат речник на настройките по всяко време чрез request.registry.settings?


РЕДАКТИРАНЕ

Благодаря, Майкъл и Крис.

Най-накрая разбирам защо Pyramid използва променливи на нишки (регистър и заявка), по-специално обект на регистър за повече от едно Pyramid приложения.

По мое мнение обаче настройките за внедряване обикновено засягат бизнес логиката, която може да дефинира специфични за приложението неща. Тези логики обикновено се поставят в един или повече модули на Python, които може да са различни от „app/init.py“ или „app/views.py“, които могат лесно да получат достъп до Config() или Registry( ). Тези модули на Python обикновено са „глобални“ на ниво процес на Python.

Това означава, че дори когато съществуват повече от едно Pyramid приложения, въпреки техните собствени нишкови локални променливи, те трябва да споделят онези „глобални“ модули на Python, които може да съдържат специфични за приложението неща на ниво процес на Python.

Разбира се, всеки от тези модули може да има "initialize()" callalbe, който се извиква с Configurator() от "main" callable на приложението, или предаване на Registry() или Request() обект през толкова дълги серии от извиквания на функции може да се срещне с обичайните изисквания. Но предполагам, че начинаещите в Pyramid (като мен) или разработчиците, които имат „голямо приложение или толкова много настройки“, може да се почувстват обезпокоителни, въпреки че това е дизайнът на Pyramid.

Така че мисля, че Registry().settings трябва да има само реални „локални за нишки“ променливи и не трябва да има нормални настройки на бизнес логиката. Отговорността за разделянето на множество специфични за приложението модули, класове, променливи за извикване и т.н. трябва да се поеме от разработчика. Засега, от моя гледна точка, ще приема отговора на Крис. Или в "main" callable, направете "execfile('settings.py', settings, settings)" и го поставете в някакво "глобално" пространство.


person takaomag    schedule 28.06.2011    source източник


Отговори (3)


Друг вариант, ако харесвате глобална конфигурация чрез Python, създайте файл settings.py. Ако се нуждае от стойности от ini файла, анализирайте ini файла и ги вземете (в обхвата на модула, така че да се изпълнява по време на импортиране):

from paste.deploy.loadwsgi import appconfig
config = appconfig('config:development.ini', 'myapp', relative_to='.')

if config['env'] == 'production':
    api_endpoint_uri = 'http://api.external.com/?{0}'
    timezone = timezone('US/Eastern')
# .. and so on ...

'config:development.ini' е името на ini файла (с префикс 'config:'). 'myapp' е името на раздела в конфигурационния файл, представляващ вашето приложение (напр. [app:myapp]). "relative_to" е името на директорията, в която може да бъде намерен конфигурационният файл.

person Chris McDonough    schedule 29.06.2011
comment
Благодаря ти! Приемам вашето решение. Причината е описана по-горе в частта РЕДАКТИРАНЕ. (Моля, позволете ми да редактирам поради дългото описание) - person takaomag; 30.06.2011

Моделът, който използвам, е да предам Configurator на модули, които трябва да бъдат инициализирани. Pyramid не използва никакви глобални променливи, тъй като целта на дизайна е да могат да се изпълняват множество екземпляри на Pyramid в един и същи процес. Нишките locals са глобални, но те са локални за текущата заявка, така че различни приложения на Pyramid могат да изпращат към тях едновременно от различни нишки.

Имайки това предвид, ако искате речник с глобални настройки, ще трябва да се погрижите сами за това. Можете дори сами да бутнете системния регистър към threadlocal мениджъра, като извикате config.begin().

Мисля, че основното нещо, което трябва да вземете тук, е, че не трябва да извиквате get_current_registry() на ниво модул, защото по време на импортирането не сте наистина гарантирани, че threadlocals са инициализирани, но във вашата init_model() функция, ако извикате get_current_registry() , ще се оправиш, ако преди си се обадил на config.begin().

Съжаляваме, че това е малко объркано, но това е често срещан въпрос и най-добрият отговор е: предайте конфигуратора на вашите подмодули, които се нуждаят от него, и им позволете да добавят неща към регистъра/обектите за настройки за използване по-късно.

person Michael Merickel    schedule 29.06.2011
comment
Благодаря Ви за отговора. От моя гледна точка отговорът на Крис е за предпочитане. Причината е описана по-горе в частта РЕДАКТИРАНЕ. (Моля, позволете ми да редактирам поради дългото описание. - person takaomag; 30.06.2011
comment
Бихте ли обяснили малко по-подробно предаването на конфигуратора към модулите? Как да го предам на моя models.py? - person GhotiPhud; 26.08.2012
comment
Препоръчителният начин за разширяване на Pyramid приложенията е чрез config.include машината, която ще извика външен метод и ще му предаде конфигурационния обект. Това е чудесен начин да изтеглите всичките си модули и да им позволите да се конфигурират сами по време на стартиране. - person Michael Merickel; 27.08.2012
comment
Проблемът ми е, че искам конфигурирана стойност вътре в @view_config декоратор. Мога ли да го направя? - person Rob Grant; 12.06.2015
comment
Не, не можете да направите това точно сега. Pyramid 1.7 може да има функция като тази, но ако използвате декоратори, тогава трябва да знаете техните настройки преди време, защото те са дефинирани в module-scope. - person Michael Merickel; 24.06.2015

Pyramid използва статична конфигурация от PasteDeploy, за разлика от Django. Вашата част [EDIT] е хубаво решение, мисля, че Pyramid общността трябва да обмисли такова използване.

person nyan    schedule 05.08.2011