Python 在Pyramid.ini文件中配置不同的电子邮件后端

Python 在Pyramid.ini文件中配置不同的电子邮件后端,python,pyramid,Python,Pyramid,我正在开发一个相当基本的应用程序。该应用程序包括发送电子邮件的功能。目前,我打算使用Sendgrid实现这一目的,但不想将其耦合得太紧。此外,我不希望在开发或测试期间发送任何电子邮件。我的解决方案是为每个提供者创建轻量级中间件类,所有这些类都提供一个send()方法 我想象通过使用对象可以实现松耦合,但我还没有完全做到 如果给定以下代码(注意,没有请求,因为我希望能够通过以下方式调用此请求): 假设my development.ini包含类似于my_app.dumptoconsolemailer

我正在开发一个相当基本的应用程序。该应用程序包括发送电子邮件的功能。目前,我打算使用Sendgrid实现这一目的,但不想将其耦合得太紧。此外,我不希望在开发或测试期间发送任何电子邮件。我的解决方案是为每个提供者创建轻量级中间件类,所有这些类都提供一个
send()
方法

我想象通过使用对象可以实现松耦合,但我还没有完全做到

如果给定以下代码(注意,没有
请求
,因为我希望能够通过以下方式调用此请求):


假设my development.ini包含类似于my_app.dumptoconsolemailer的东西,
get\u emailer()
函数会是什么样子?

你提到芹菜会改变一切。。。芹菜并不是很明显,但芹菜工人是一个完全独立的过程,对金字塔应用程序一无所知,可能运行在不同的机器上,在web应用程序创建任务数小时后执行任务——工人只是从队列中一个接一个地执行任务。没有请求,没有配置程序,没有WSGI堆栈,没有从
.ini
文件组装应用程序的PasteDeploy

要点是——芹菜工人不知道您的金字塔过程是在开发还是生产配置中启动的,除非您明确告诉它。甚至可以让一个工人从两个应用程序执行任务,一个在开发模式下运行,另一个在生产模式下运行:)

一个选项是在启动时显式地将配置传递给芹菜工作者(比如,在celeryconfig.py中声明一些变量)。然后,一个工作人员将始终使用相同的邮件处理所有任务

另一个选项是为每个任务将“mailer_type”参数从您的Pyramid应用程序显式传递给工作者:

@task
def send_email(sender, recipient, subject, contents, mailer_type='dummy'):
    emailer = get_emailer(mailer_type)
    emailer.send(from=sender, to=receipient, subject=subject, body=contents)
在您的Pyramid应用程序中,您可以将任何键/值对放入
.ini
文件中,并通过
request.registry.settings
访问它们:

send_email.delay(..., request.registry.settings['mailer_type'])

自从一个月前提出这个问题以来,我读了一些书。这让我想到了两种可能的解决方案:

A) 惯用金字塔 我们要解决两个问题:

  • 如何为Pyramid应用全局设置在PasteDeploy配置文件(.ini文件)中指定的类
  • 如何在运行时访问我们的类而无需“通过”请求
  • 要设置指定的类,我们应该在模块中定义一个
    includeme()
    函数,然后在.ini文件中将该模块指定为
    pyramid.includes
    的一部分。然后,在我们的
    includeme()
    函数中,我们使用的一部分
    config.registry.registerUtility()
    来注册我们的类及其实现的

    为了在运行时访问我们的类,我们需要调用
    registry.queryUtility()
    ,从
    pyramid.threadlocal.get\u current\u registry()
    获取
    registry

    这个解决方案有点像黑客,因为它使用
    threadlocal
    来获取配置

    B) 惰性模全局 我个人的问题解决方案更简单(可能不是线程安全的):


    谢谢你的回答。我没有将其标记为已接受,尽管它建议使用
    Request
    获取注册表设置。可能我不太清楚:最后一个代码示例是从金字塔过程调用的,您确实可以访问请求。从那里,您可以显式地将一些变量传递给
    send_email
    任务(通过调用send_email.delay(…)),以便在工作进程中执行该任务时,它可以确定要使用哪个邮件程序。@Sergey我现在遇到了相同的问题。你对这个问题做了什么?这是怎么被接受的答案?它没有说明如何实现
    get\u emailer
    函数?@shreading:答案非常详细,说明了如何修改现有的get\u emailer()函数,以便它可以使用不同的电子邮件后端,方法是从金字塔过程中传递一个参数。从这个问题来看,OP似乎已经有了这个函数,它总是使用相同的后端-修改这个函数只需添加几个if/else语句。如果
    get\u emailer
    函数的具体实现取决于实际使用的邮件程序(OP将使用Sendgrid),这在很大程度上与问题无关。我看不出这两种解决方案在工作进程中如何工作。如何在工作进程中调用
    includeme()
    ,以及它接收的
    config
    参数来自何处?
    send_email.delay(..., request.registry.settings['mailer_type'])
    
    # In module MailerHolder:
    class Holder(object):
        mailer = None
    
    holder = Holder()
    
    def get_emailer():
        return holder.mailer
    
    #In module ConsoleMailer:
    import MailHolder
    
    class ConsoleMailer(object):
        def send(self, **kwargs):
        # Code to print email to console
    
    def includeme(config):
        MailHolder.holder.mailer = ConsoleMailer()