code: def filterstr_to_filterfunc(filter_str: str, item_type: type):
      
  
      # The filter_str is parsed, then all names occurring in its AST are replaced by loads to post.<name>. A
      # function Post->bool is returned which evaluates the filter with the post as 'post' in its namespace.
  
      class TransformFilterAst(ast.NodeTransformer):
          def visit_Name(self, node: ast.Name):
              # pylint:disable=no-self-use
              if not isinstance(node.ctx, ast.Load):
                  raise InvalidArgumentException("Invalid filter: Modifying variables ({}) not allowed.".format(node.id))
              if node.id == "datetime":
                  return node
              if not hasattr(item_type, node.id):
                  raise InvalidArgumentException("Invalid filter: {} not a {} attribute.".format(node.id,
                                                                                                 item_type.__name__))
              new_node = ast.Attribute(ast.copy_location(ast.Name('item', ast.Load()), node), node.id,
                                       ast.copy_location(ast.Load(), node))
              return ast.copy_location(new_node, node)
  
      input_filename = '<command line filter parameter>'
      compiled_filter = compile(TransformFilterAst().visit(ast.parse(filter_str, filename=input_filename, mode='eval')),
                                filename=input_filename, mode='eval')
  
      def filterfunc(item) -> bool:
          # pylint:disable=eval-used
          return bool(eval(compiled_filter, {'item': item, 'datetime': datetime.datetime}))
  
      return filterfunc
docs: Takes an --post-filter=... or --storyitem-filter=... filter
       specification and makes a filter_func Callable out of it.