2013-09-05 43 views
0

我在我的Django应用程序,用户定义的异常:Django的:用户定义的异常不被抓住

class TemplateMatchError(Exception): 
    ... 

我的代码段,捕捉这个异常定期try ... except

try: 
    if template.match(text): 
    return attrs 
except TemplateMatchError as e: 
    continue 

我注意到在生产中,当DEBUG=True,这个错误没有被捕获,如果提出,我的浏览器显示黄色的Django堆栈跟踪页面。当DEBUG=False,异常被捕获。

我对这种行为感到惊讶,因为这意味着调试设置会改变普通python try...except的行为。这是一个正确的解释,如果是的话,为什么Django这样工作?

UPDATE:根据意见,我下面张贴实际回溯(名称是从上面玩具例子不同):

Environment: 


Request Method: POST 
Request URL: http://mysite.com/api/call/6/ 

Django Version: 1.4.2 
Python Version: 2.7.3 
Installed Applications: 
('longerusername', 
'django.contrib.auth', 
'django.contrib.contenttypes', 
'django.contrib.sessions', 
'django.contrib.messages', 
'django.contrib.sites', 
'django.contrib.admin', 
'south', 
'django_extensions', 
'django.contrib.staticfiles', 
'crispy_forms', 
'api', 
'rest_framework') 
Installed Middleware: 
('django.middleware.common.CommonMiddleware', 
'django.contrib.sessions.middleware.SessionMiddleware', 
'django.middleware.csrf.CsrfViewMiddleware', 
'django.contrib.auth.middleware.AuthenticationMiddleware', 
'django.contrib.messages.middleware.MessageMiddleware') 


Traceback: 
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response 
    111.       response = callback(request, *callback_args, **callback_kwargs) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/compat.py" in view 
    121.     return self.dispatch(request, *args, **kwargs) 
File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py" in wrapped_view 
    77.   return view_func(*args, **kwargs) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py" in dispatch 
    327.    response = self.handle_exception(exc) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py" in dispatch 
    324.    response = handler(request, *args, **kwargs) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/generics.py" in put 
    469.   return self.update(request, *args, **kwargs) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/mixins.py" in update 
    129.   if serializer.is_valid(): 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in is_valid 
    479.   return not self.errors 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in errors 
    471.     ret = self.from_native(data, files) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in from_native 
    867.   instance = super(ModelSerializer, self).from_native(data, files) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in from_native 
    319.     attrs = self.perform_validation(attrs) 
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in perform_validation 
    260.      attrs = validate_method(attrs, source) 
File "/home/uname/api/serializers.py" in validate_text 
    68.     if template.match(text): 
File "/home/uname/api/models.py" in match 
    135.    raise TemplateMatchError() 

Exception Type: TemplateMatchError at /api/call/6/ 
Exception Value: Not a match 

这里是我的models.py:

import re 
from datetime import datetime 
from django.db import models 
from django.contrib.auth.models import User as AuthUser 
from otalo.ao.models import User 
from otalo.surveys.models import Survey 

class XCall(models.Model): 
    created_on = models.DateTimeField(auto_now_add=True) 
    send_on = models.DateTimeField(default=datetime.now) 
    auth_user = models.ForeignKey(AuthUser, related_name='calls') 
    recipient = models.ForeignKey(User) 
    text = models.CharField(max_length=4096) 
    backup_calls = models.IntegerField(blank=True, null=True) 

    ''' 
    ' Associate with the survey, since 
    ' all the calls can be traced through it. 
    ' This billing/auditing purposes. 
    ' 
    ' This can be lazily created at the time of 
    ' scheduling the call, so make it nullable 
    ' 
    ''' 
    survey = models.ForeignKey(Survey, null=True, blank=True) 


    def __unicode__(self): 
     return unicode(self.auth_user) + '-' + unicode(self.recipient) 

class TemplateMatchError(Exception): 
    STD_MESSAGE = 'Not a match' 
    def __init__(self, msg=None): 
     if msg is not None: 
      self.msg = TemplateMatchError.STD_MESSAGE + ': ' + msg 
     else: 
      self.msg = TemplateMatchError.STD_MESSAGE 
    def __str__(self): 
     return self.msg 

class XTemplate(models.Model): 

    VARTYPE_NUM = '_N_' 
    VARTYPE_WORD = '_W_' 
    VARTYPE_DATETIME = '_DT_' 
    VARTYPE_DATE = '_D_' 
    VARTYPE_TIME = '_T_' 

    # add grouping for regexes for easy extraction 
    VARTYPE_REGEXS = { VARTYPE_NUM: r'(\d+(?:\.\d{1,2})?)', # supports decimals up to two precision points. Could be more but then 
                  # the prompting would start to sound weird 
         VARTYPE_WORD: r'(\w+)', 
         # Match dates and times as words 
         # so that your match function can 
         # try to convert to datetime for more 
         # detailed error messages 
         VARTYPE_DATETIME: r'(\w+)', 
         VARTYPE_DATE: r'(\w+)', 
         VARTYPE_TIME: r'(\w+)', 
         } 

    DATE_FORMATS = { 
         VARTYPE_DATETIME: '%d-%m-%y %H:%M', 
         VARTYPE_DATE: '%d-%m-%y', 
         VARTYPE_TIME: '%H:%M' 
        } 

    created_on = models.DateTimeField(auto_now_add=True) 
    auth_user = models.ForeignKey(AuthUser, related_name='templates') 
    ''' 
    ' Encodes the wildcards and their type. 
    ' e.g. Your account number _N_ is of type _W_ expiring at time _D_ 
    ''' 
    text = models.CharField(max_length=4096) 

    ''' 
    ' For common prompts like numbers and dates and times 
    ''' 
    language = models.CharField(max_length=24) 

    STATUS_PENDING = 0 
    STATUS_ACTIVE = 1 
    STATUS_INACTIVE = 2 

    STATUSES = (
    (STATUS_PENDING, 'Pending'), 
    (STATUS_ACTIVE, 'Active'), 
    (STATUS_INACTIVE, 'Inactive'), 
    ) 
    status = models.IntegerField(choices=STATUSES) 

    ''' 
    ' Compare the inupt text to this template's text; 
    ' return the match object if it matches, else throw an error 
    ''' 
    def match(self, input): 
     pattern = self.text 

     # first convert the template pattern into a regular expression 
     vars = [var.group() for var in re.finditer('_[A-Z]_', pattern)] 
     re_pattern = pattern 
     for vartype, regex in XTemplate.VARTYPE_REGEXS.iteritems(): 
      re_pattern = re_pattern.replace(vartype, regex) 

     # now try an initial match of the structure of the input 
     match = re.match(re_pattern, input) 

     if match: 
      # make sure words are in the wordlist 
      # numbers are valid numbers 
      # and dates are in the proper format 
      vocab = [word.text for word in self.vocabulary.all()] 
      vals = match.groups() 
      for i in range(len(vars)): 
       if i > len(vals): 
        raise TemplateMatchError('Missing a variable in input') 
       var = vars[i] 
       val = vals[i] 
       if var == XTemplate.VARTYPE_NUM: 
        try: 
         float(val) 
        except ValueError as e: 
         raise TemplateMatchError('Invalid number') 
       elif var == XTemplate.VARTYPE_WORD: 
        if val not in vocab: 
         raise TemplateMatchError('Word not in vocabulary') 
       elif var == XTemplate.VARTYPE_DATETIME or var == XTemplate.VARTYPE_DATE or var == XTemplate.VARTYPE_TIME: 
        format = XTemplate.DATE_FORMATS[var] 
        try: 
         date = datetime.strptime(val, format) 
        except ValueError as e: 
         raise TemplateMatchError('Invalid date, time, or datetime format - ' + val) 
      return match 
     else: 
      raise TemplateMatchError() 

    def __unicode__(self): 
     return self.text + '-' + unicode(self.auth_user) 

class XWord(models.Model): 
    text = models.CharField(max_length=128) 
    template = models.ForeignKey(XTemplate, related_name='vocabulary') 

    def __unicode__(self): 
     return self.text 

串类问题:

class CallSerializer(serializers.HyperlinkedModelSerializer): 
    url = serializers.HyperlinkedIdentityField(
     view_name='call-detail', 
    ) 
    recipient = PhoneNumberField(read_only=False) 
    status = SurveySerializer(source='survey', read_only=True) 

    def validate_text(self, attrs, source): 
     text = attrs['text'] 
     auth = self.context['request'].user 
     templates = auth.templates.all() 
     for template in templates: 
      try: 
       if template.match(text): 
        return attrs 
      except TemplateMatchError as e: 
       continue 

     raise serializers.ValidationError("Call text does not match a registered template") 

    class Meta: 
     model = XCall 
     fields = ('url', 'id', 'text', 'recipient', 'send_on', 'backup_calls', 'status') 
     lookup_field= 'pk' 
+4

可能还有其他事情发生,请发布完整的回溯(这可能是中间件等)。 –

+0

答案是NO。例外情况不以这种方式实施。但是,更改该值可能会影响代码的结果,如变量和流量控制。 – stormlifter

+0

@ThomasOrozco追踪添加到问题。谢谢! – Neil

回答

1

问题在于models.py抛出了一个不同的异常类,但名称相同。

我的settings.py没有指定应用程序的完整路径,其中有问题的models.py。指定完整路径后,异常类匹配并捕获异常。感谢所有提供了很好提示的人。

+0

您是否发现它与“DEBUG = True”相关? – hynekcer

+0

不,我没有。这仍然是一个谜,因此包括我在内的这些答案都不完整 – Neil

0

这将有助于看到更多关于异常捕获的代码。从你出什么,有几件事情来看待:

  1. 我假设TemplateMatchError是你原来叫什么MyError
  2. 你的代码似乎不确定如何template.match返回否定结果。在serializers.py,它似乎期望nil/false返回值,但函数本身引发异常,而不是返回falsey。
  3. 您已经显示它的代码段有不好的缩进,这可能会导致失败捕获错误。

正如你已经证明它:

try: 
    template.match(text) 
    # Do other stuff, presumably including this: 
    try: 
     somethingElse() 
    except TemplateMatchError as e: 
     #this won't catch exceptions from template.match(text) 
     continue 

我怎么觉得你的意思是:

try: 
    template.match(text) 
except TemplateMatchError as e: 
    # This will be caught 
    continue 

希望有所帮助。

+0

我已经在上面的问题中提供了确切的代码。这不是缩进错误,请注意,当生产中的Debug = False时,一切都按预期工作。当Debug = True时,问题就会出现。我知道我不应该在生产中将其设置为True,但我试图理解为什么导致此行为。 – Neil

+0

你可以多显示一下'serializers.py'和'areodels.py'吗? – Hbcdev

+0

以上添加@Hbcdev – Neil

0

你确定它是从你提出的同一个模块导入的同一个类«TemplateMatchError»,你试图捕获。

如果这是两个具有相同名称但从不同模块导入的类,则python不会将它们作为相同的Exception异常,否则永远不会进入您的catch块。

+0

是的,我证实他们是一样的。再次,我猜这是一个问题,无论是DEBUG设置为True或False – Neil

0

以这种方式修改代码以验证非常接近的假设。

import api 
    assert TemplateMatchError == api.models.TemplateMatchError 
    try: 
     if template.match(text): 
      return attrs 
    except TemplateMatchError as e: 
     continue 
    except Exception as e: 
     assert isinstance(e, TemplateMatchError) 
     import pdb; pdb.set_trace() 
     pass # if both asserts vere OK, but the exception is uncaught (not 
     #  probable) you are here and see the the line debugger started 
     raise # continue by caugting in external frames 

开始以最好的方式TESTSERVER用于调试
python manage.py runserver --nothreading --noreload
当你看到调试提示(Pdb),把这些命令,以便重复一步一步来:

l(ist) Enter 
j(ump) <line number of the line 'try:'> Enter 
b /home/uname/api/models.py:135 Enter # breakpoint at that raise command 
c(ontinue) Enter 
s(tep) Enter # press Enter five times to see steps, how the lines 
       # "except ... continue" are missed 
c(ontinue) # to reraise and see an error page in the browser 

不过我如果DEBUG = True,那么认为其中一个断言会失败,并且您将了解更多信息,而无需调试器。

+0

谢谢,这给了我需要的提示。我调试发现抛出的异常不是我的Django应用程序的完整路径。我修改了我的settings.py来引用应用程序的全名,它解决了问题 – Neil