openpyxl Value must be either numerical or a string containing a wildcard

2023-03-06
#python

前言

openpyxl 在 3.1.0 之后版本引入了一个新的 bug,具体问题可以参考 issue-1959。一旦你使用的 Excel 文件中的通配符(wildcard) 不存在数字的话,就会如下的错误:

    File "/usr/local/lib/python3.8/site-packages/openpyxl/worksheet/filters.py", line 165, in __set__
      raise ValueError("Value must be either numerical or a string containing a wildcard")
  ValueError: Value must be either numerical or a string containing a wildcard

造成影响的关键代码如下:

  # https://foss.heptapod.net/openpyxl/openpyxl/-/blob/branch/3.1/openpyxl/worksheet/filters.py
  
  class CustomFilterValueDescriptor(Convertible):
      """
      Excel uses wildcards for string matching
      """
  
      pattern = re.compile(r"\d+|^\*.+|^.+\*$")
      expected_type = float
  
      def __set__(self, instance, value):
          if isinstance(value, str):
              m = self.pattern.match(value)
              if not m:
                  raise ValueError("Value must be either numerical or a string containing a wildcard")
              if "*" in value:
                  self.expected_type = str
          super().__set__(instance, value)
  
  

其中的 pattern 表达式存在问题。

解决办法

方法一 将 openpyxl 的版本回到到 3.0.10 即可解决

在这个版本之前没有引入 CustomFilterValueDescriptor,对通配符没有做任何校验。

方法二 Monkey patch

使用猴子补丁,重载 CustomFilterValueDescriptor 这个类方法,然后等待官方升级解决问题。

  from openpyxl.worksheet.filters import CustomFilterValueDescriptor
  
  def monkey_set(self,instance,value):
      pass
  
  CustomFilterValueDescriptor.__set__ = monkey_set

参考链接

  1. Monkey patch
  2. openpyxl-issue-1959