prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  action alias regex fails to evaluate null optional arguments when type is not string
  For example, something like:  `update {{hostname}}( {{count}} times)?` 
  Works for `!update myhost 2 times`
  Fails for `!update myhost`
  Error: `(invalid literal for int() with base 10: '')`
  So the workaround is to change the type of `count` from integer to string.
  Or @emedvedev provides another workaround:
  
  ```
  - update {{ hostname }} {{ count }} times
  - update {{ hostname }}
  ```
  
  Start from the most explicit.
  
  
  </issue>
  <code>
  [start of st2common/st2common/models/utils/action_alias_utils.py]
  1 # Licensed to the StackStorm, Inc ('StackStorm') under one or more
  2 # contributor license agreements.  See the NOTICE file distributed with
  3 # this work for additional information regarding copyright ownership.
  4 # The ASF licenses this file to You under the Apache License, Version 2.0
  5 # (the "License"); you may not use this file except in compliance with
  6 # the License.  You may obtain a copy of the License at
  7 #
  8 #     http://www.apache.org/licenses/LICENSE-2.0
  9 #
  10 # Unless required by applicable law or agreed to in writing, software
  11 # distributed under the License is distributed on an "AS IS" BASIS,
  12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 # See the License for the specific language governing permissions and
  14 # limitations under the License.
  15 
  16 import re
  17 from st2common.exceptions import content
  18 
  19 __all__ = [
  20     'ActionAliasFormatParser'
  21 ]
  22 
  23 
  24 class ActionAliasFormatParser(object):
  25 
  26     def __init__(self, alias_format=None, param_stream=None):
  27         self._format = alias_format or ''
  28         self._param_stream = param_stream or ''
  29 
  30     def get_extracted_param_value(self):
  31 
  32         result = {}
  33 
  34         # As there's a lot of questions about using regular expressions,
  35         # I'll try to be thorough when documenting this code.
  36 
  37         # We're parsing the arbitrary key-value pairs at the end of the stream
  38         # to support passing of parameters not specified in the format string,
  39         # and cutting them from the stream as they're no longer needed.
  40         # Possible values are quoted strings, a word, or anything inside "{}".
  41         pairs_match = r'(?:^|\s+)(\S+)=("(.*?)"|\'(.*?)\'|({.*?})|(\S+))'
  42         extra = re.match(r'.*?((' + pairs_match + r'\s*)*)$',
  43                          self._param_stream, re.DOTALL)
  44         if extra:
  45             kv_pairs = re.findall(pairs_match,
  46                                   extra.group(1), re.DOTALL)
  47             self._param_stream = self._param_stream.replace(extra.group(1), '')
  48         self._param_stream = " %s " % self._param_stream
  49 
  50         # Now we'll match parameters with default values in form of
  51         # {{ value = parameter }} (and all possible permutations of spaces),
  52         # compiling them into a list.
  53         # "test {{ url = http://google.com }} {{ extra = Test }}" will become
  54         # [ ["url", "http://google.com"], ["extra", "Test"] ]
  55         params = re.findall(r'{{\s*(.+?)\s*(?:=\s*[\'"]?({.+?}|.+?)[\'"]?)?\s*}}',
  56                             self._format, re.DOTALL)
  57 
  58         # Now we're transforming our format string into a regular expression,
  59         # substituting {{ ... }} with regex named groups, so that param_stream
  60         # matched against this expression yields a dict of params with values.
  61         param_match = r'["\']?(?P<\2>(?:(?<=\').+?(?=\')|(?<=").+?(?=")|{.+?}|.+?))["\']?'
  62         reg = re.sub(r'(\s*){{\s*([^=}]+?)\s*}}(?![\'"]?\s+}})',
  63                      r'\1' + param_match,
  64                      self._format)
  65         reg = re.sub(r'(\s*){{\s*(\S+)\s*=\s*(?:{.+?}|.+?)\s*}}',
  66                      r'(?:\1' + param_match + r')?',
  67                      reg)
  68         reg = re.sub(r'(\s*){{\s*(.+?)\s*}}',
  69                      r'\1' + param_match,
  70                      reg)
  71         reg = '^\s*' + reg + r'\s*$'
  72 
  73         # Now we're matching param_stream against our format string regex,
  74         # getting a dict of values. We'll also get default values from
  75         # "params" list if something is not present.
  76         # Priority, from lowest to highest:
  77         # 1. Default parameters
  78         # 2. Matched parameters
  79         # 3. Extra parameters
  80         matched_stream = re.match(reg, self._param_stream, re.DOTALL)
  81         if matched_stream:
  82             values = matched_stream.groupdict()
  83         for param in params:
  84             matched_value = values[param[0]] if matched_stream else None
  85             result[param[0]] = matched_value or param[1]
  86         if extra:
  87             for pair in kv_pairs:
  88                 result[pair[0]] = ''.join(pair[2:])
  89 
  90         if self._format and not (self._param_stream.strip() or any(result.values())):
  91             raise content.ParseException('No value supplied and no default value found.')
  92 
  93         return result
  94
  [end of st2common/st2common/models/utils/action_alias_utils.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/st2common/st2common/models/utils/action_alias_utils.py b/st2common/st2common/models/utils/action_alias_utils.py
  --- a/st2common/st2common/models/utils/action_alias_utils.py
  +++ b/st2common/st2common/models/utils/action_alias_utils.py
  @@ -82,7 +82,9 @@
               values = matched_stream.groupdict()
           for param in params:
               matched_value = values[param[0]] if matched_stream else None
  -            result[param[0]] = matched_value or param[1]
  +            matched_result = matched_value or param[1]
  +            if matched_result:
  +                result[param[0]] = matched_result
           if extra:
               for pair in kv_pairs:
                   result[pair[0]] = ''.join(pair[2:])
  
verification_info: {"golden_diff": "diff --git a/st2common/st2common/models/utils/action_alias_utils.py b/st2common/st2common/models/utils/action_alias_utils.py\n--- a/st2common/st2common/models/utils/action_alias_utils.py\n+++ b/st2common/st2common/models/utils/action_alias_utils.py\n@@ -82,7 +82,9 @@\n             values = matched_stream.groupdict()\n         for param in params:\n             matched_value = values[param[0]] if matched_stream else None\n-            result[param[0]] = matched_value or param[1]\n+            matched_result = matched_value or param[1]\n+            if matched_result:\n+                result[param[0]] = matched_result\n         if extra:\n             for pair in kv_pairs:\n                 result[pair[0]] = ''.join(pair[2:])\n", "issue": "action alias regex fails to evaluate null optional arguments when type is not string\nFor example, something like:  `update {{hostname}}( {{count}} times)?` \nWorks for `!update myhost 2 times`\nFails for `!update myhost`\nError: `(invalid literal for int() with base 10: '')`\nSo the workaround is to change the type of `count` from integer to string.\nOr @emedvedev provides another workaround:\n\n```\n- update {{ hostname }} {{ count }} times\n- update {{ hostname }}\n```\n\nStart from the most explicit.\n\n", "before_files": [{"content": "# Licensed to the StackStorm, Inc ('StackStorm') under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nfrom st2common.exceptions import content\n\n__all__ = [\n    'ActionAliasFormatParser'\n]\n\n\nclass ActionAliasFormatParser(object):\n\n    def __init__(self, alias_format=None, param_stream=None):\n        self._format = alias_format or ''\n        self._param_stream = param_stream or ''\n\n    def get_extracted_param_value(self):\n\n        result = {}\n\n        # As there's a lot of questions about using regular expressions,\n        # I'll try to be thorough when documenting this code.\n\n        # We're parsing the arbitrary key-value pairs at the end of the stream\n        # to support passing of parameters not specified in the format string,\n        # and cutting them from the stream as they're no longer needed.\n        # Possible values are quoted strings, a word, or anything inside \"{}\".\n        pairs_match = r'(?:^|\\s+)(\\S+)=(\"(.*?)\"|\\'(.*?)\\'|({.*?})|(\\S+))'\n        extra = re.match(r'.*?((' + pairs_match + r'\\s*)*)$',\n                         self._param_stream, re.DOTALL)\n        if extra:\n            kv_pairs = re.findall(pairs_match,\n                                  extra.group(1), re.DOTALL)\n            self._param_stream = self._param_stream.replace(extra.group(1), '')\n        self._param_stream = \" %s \" % self._param_stream\n\n        # Now we'll match parameters with default values in form of\n        # {{ value = parameter }} (and all possible permutations of spaces),\n        # compiling them into a list.\n        # \"test {{ url = http://google.com }} {{ extra = Test }}\" will become\n        # [ [\"url\", \"http://google.com\"], [\"extra\", \"Test\"] ]\n        params = re.findall(r'{{\\s*(.+?)\\s*(?:=\\s*[\\'\"]?({.+?}|.+?)[\\'\"]?)?\\s*}}',\n                            self._format, re.DOTALL)\n\n        # Now we're transforming our format string into a regular expression,\n        # substituting {{ ... }} with regex named groups, so that param_stream\n        # matched against this expression yields a dict of params with values.\n        param_match = r'[\"\\']?(?P<\\2>(?:(?<=\\').+?(?=\\')|(?<=\").+?(?=\")|{.+?}|.+?))[\"\\']?'\n        reg = re.sub(r'(\\s*){{\\s*([^=}]+?)\\s*}}(?![\\'\"]?\\s+}})',\n                     r'\\1' + param_match,\n                     self._format)\n        reg = re.sub(r'(\\s*){{\\s*(\\S+)\\s*=\\s*(?:{.+?}|.+?)\\s*}}',\n                     r'(?:\\1' + param_match + r')?',\n                     reg)\n        reg = re.sub(r'(\\s*){{\\s*(.+?)\\s*}}',\n                     r'\\1' + param_match,\n                     reg)\n        reg = '^\\s*' + reg + r'\\s*$'\n\n        # Now we're matching param_stream against our format string regex,\n        # getting a dict of values. We'll also get default values from\n        # \"params\" list if something is not present.\n        # Priority, from lowest to highest:\n        # 1. Default parameters\n        # 2. Matched parameters\n        # 3. Extra parameters\n        matched_stream = re.match(reg, self._param_stream, re.DOTALL)\n        if matched_stream:\n            values = matched_stream.groupdict()\n        for param in params:\n            matched_value = values[param[0]] if matched_stream else None\n            result[param[0]] = matched_value or param[1]\n        if extra:\n            for pair in kv_pairs:\n                result[pair[0]] = ''.join(pair[2:])\n\n        if self._format and not (self._param_stream.strip() or any(result.values())):\n            raise content.ParseException('No value supplied and no default value found.')\n\n        return result\n", "path": "st2common/st2common/models/utils/action_alias_utils.py"}]}