2017-08-08 170 views
5

我看到这样的警告,在我的日志:堆栈跟踪的UserWarning

py.warnings.__init__: WARNING .../bs4/__init__.py:219: UserWarning: "foo" 
    looks like a filename, not markup. You should probably open this file 
    and pass the filehandle into Beautiful Soup 

此消息不会很大帮助。

我希望看到堆栈跟踪发生这种情况。

请不要看这个警告的内容。这个问题是不是美丽的汤:-)

一个简单的解决办法是修改第三方代码(bs4/__init__.py于线219),并添加这样的:

import traceback 
logger.warn('Exc at ...\n%s' % ''.join(traceback.format_stack())) 

但我想,以避免这个。理由:

  • 这是来自生产系统的警告。我不想更改来源。
  • 下一次这样的警告时,我想看看堆栈跟踪立即

有没有标志或设置为Python,我可以改变,不仅看到一条线,但同时堆栈跟踪?我需要上面的帧来调试这个。

在这个环境中Python 2.7被使用。

回答

2

你需要做到以下几点:

  1. 创建如果USER_SITE不存在:问题python -c "import site; site._script()",看到USER_SITE变量内容
  2. 位置的文件usercustomize.py与下面的代码目录:

    import traceback 
    import warnings 
    
    
    _old_warn = warnings.warn 
    def warn(*args, **kwargs): 
        tb = traceback.extract_stack() 
        _old_warn(*args, **kwargs) 
        print("".join(traceback.format_list(tb)[:-1])) 
    warnings.warn = warn 
    

    Credits to this回答代码。

像往常一样运行代码。我的测试代码:

import warnings 

def f(): 
    warnings.warn("foz") 

f() 

手术前:

$ python test_warn.py 
test_warn.py:4: UserWarning: foz 
    warnings.warn("foz") 

后:

$ python test_warn.py 
<USER_SITE_REDACTED>/usercustomize.py:6: UserWarning: foz 
    _old_warn(*args, **kwargs) 
    File "test_warn.py", line 6, in <module> 
    f() 
    File "test_warn.py", line 4, in f 
    warnings.warn("foz") 
+0

好的,我明白了。 AFAIK这被称为猴子补丁。是的,我认为usercustomize.py是做这种猴子修补的好地方。谢谢。 – guettli

+1

@guettli,它是monkeypatching。虽然这可能是使用它的合理案例,但考虑到源不会改变,并且入侵不会改变任何逻辑 - 只是有效地添加更多日志记录。 – RebelWithoutAPulse

1

如果我想找到一个警告的根本,我一般只是促进WarningsExceptions

在你的情况下,你可以简单地使用warnings.simplefilterwarnings.filterwarnings

例如:

import warnings 

def func(): 
    warnings.warn('abc', UserWarning) 

def func2(): 
    func() 

# Here I promote all UserWarnings to exceptions, but you could also use "warnings.filterwarnings" 
# to only promote warnings from a specified module or matching a specified message. 
# You may need to check which is most useful/appropriate for you. 
warnings.simplefilter("error", UserWarning) # you might need to reset this later :) 
func2() 

这给出了一个完整回溯:

--------------------------------------------------------------------------- 
UserWarning        Traceback (most recent call last) 
<ipython-input-11-be791e1071e7> in <module>() 
     8 
     9 warnings.simplefilter("error", UserWarning) 
---> 10 func2() 

<ipython-input-11-be791e1071e7> in func2() 
     5 
     6 def func2(): 
----> 7  func() 
     8 
     9 warnings.simplefilter("error", UserWarning) 

<ipython-input-11-be791e1071e7> in func() 
     2 
     3 def func(): 
----> 4  warnings.warn('abc', UserWarning) 
     5 
     6 def func2(): 

UserWarning: abc 

如果你想调试这一点,你可以很容易地钩蟒蛇pdb S于最近遇到的异常:

import pdb 

pdb.pm() 

产生:

> <ipython-input-11-be791e1071e7>(4)func() 
-> warnings.warn('abc', UserWarning) 
(Pdb) _________________ 

这会开始对最后遇到的异常进行事后分析。这应使您能够通过帧挖掘和检查变量等


你还问到一个标志,而且确实存在,使“警告处理”,该-W flag的标志。它非常像warnings.filterwarnings功能。为方便起见,我在此复制了-W国旗的文档:

Warning control. Python’s warning machinery by default prints warning messages to sys.stderr. A typical warning message has the following form:

file:line: category: message 

By default, each warning is printed once for each source line where it occurs. This option controls how often warnings are printed.

Multiple -W options may be given; when a warning matches more than one option, the action for the last matching option is performed. Invalid -W options are ignored (though, a warning message is printed about invalid options when the first warning is issued).

Starting from Python 2.7, DeprecationWarning and its descendants are ignored by default. The -Wd option can be used to re-enable them.

Warnings can also be controlled from within a Python program using the warnings module.

The simplest form of argument is one of the following action strings (or a unique abbreviation) by themselves:

  • ignore

    Ignore all warnings.

  • default

    Explicitly request the default behavior (printing each warning once per source line).

  • all

    Print a warning each time it occurs (this may generate many messages if a warning is triggered repeatedly for the same source line, such as inside a loop).

  • module

    Print each warning only the first time it occurs in each module.

  • once

    Print each warning only the first time it occurs in the program.

  • error:

    Raise an exception instead of printing a warning message.

The full form of argument is:

action:message:category:module:line 

Here, action is as explained above but only applies to messages that match the remaining fields. Empty fields match all values; trailing empty fields may be omitted. The message field matches the start of the warning message printed; this match is case-insensitive. The category field matches the warning category. This must be a class name; the match tests whether the actual warning category of the message is a subclass of the specified warning category. The full class name must be given. The module field matches the (fully-qualified) module name; this match is case-sensitive. The line field matches the line number, where zero matches all line numbers and is thus equivalent to an omitted line number.

+0

我想这不是在你的awer中解决:下次发生这种警告时,我希望立即看到堆栈跟踪 – guettli

+0

如果将它们提升为异常,您将立即看到回溯。还是我误解了你的问题? – MSeifert

+0

昨天我误解了你的解决方案。我想现在我明白了。您“引发异常而不是打印警告消息。”我想避免这种情况,因为警告发生在生产环境中,到目前为止它在开发环境中不可重复。在这种情况下,我希望避免将警告切换为异常。 – guettli