{"action":"created","issue":{"url":"https://api.github.com/repos/behave/behave/issues/383","labels_url":"https://api.github.com/repos/behave/behave/issues/383/labels{/name}","comments_url":"https://api.github.com/repos/behave/behave/issues/383/comments","events_url":"https://api.github.com/repos/behave/behave/issues/383/events","html_url":"https://github.com/behave/behave/issues/383","id":122717509,"number":383,"title":"don't crash the process if a custom_type parser fails parsing the value it received","user":{"login":"zsoldosp","id":64987,"avatar_url":"https://avatars.githubusercontent.com/u/64987?v=3","gravatar_id":"","url":"https://api.github.com/users/zsoldosp","html_url":"https://github.com/zsoldosp","followers_url":"https://api.github.com/users/zsoldosp/followers","following_url":"https://api.github.com/users/zsoldosp/following{/other_user}","gists_url":"https://api.github.com/users/zsoldosp/gists{/gist_id}","starred_url":"https://api.github.com/users/zsoldosp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zsoldosp/subscriptions","organizations_url":"https://api.github.com/users/zsoldosp/orgs","repos_url":"https://api.github.com/users/zsoldosp/repos","events_url":"https://api.github.com/users/zsoldosp/events{/privacy}","received_events_url":"https://api.github.com/users/zsoldosp/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-12-17T11:30:35Z","updated_at":"2015-12-18T22:57:47Z","closed_at":null,"body":"## Problem summary\r\n```python\r\nfrom behave import register_type\r\ndef my_custom_type(text):\r\n    raise Exception('could not parse text')\r\nregister_type(Thing=my_custom_type)\r\n```\r\nif we have the above custom type registered, when running a set of feature files, as soon as this type is attempted to be parsed for a step, the whole `behave` process crashes and no report is printed. \r\n\r\nI think it shouldn't crash the process.\r\n\r\nBelow is a feature file reproducing the issue, as well as a sketch of a potential solution (which isn't yet a full solution) - I'm happy to work on this to develop this into a pull request with some guidance from a core developer\r\n\r\n----\r\n## Repro feature file\r\n\r\n`issue.features/issueXXXX.feature`\r\n```\r\n@issue\r\nFeature: Issue #XXX: Exception from a step parser can crash the whole behave process\r\n\r\n  Scenario: Does not crash the process\r\n    Given a new working directory\r\n    And   a file named \"features/environment.py\" with:\r\n      \"\"\"\r\n      from behave import register_type\r\n\r\n      def my_custom_type(text):\r\n          raise Exception('could not parse text')\r\n\r\n      register_type(Thing=my_custom_type)\r\n      \"\"\"\r\n    And   a file named \"features/steps/steps.py\" with:\r\n      \"\"\"\r\n      @when(u'a custom thing {something:Thing} is created')\r\n      def step_impl(context, something):\r\n          pass\r\n\r\n      @then(u'all is OK')\r\n      def step_impl(context):\r\n          pass\r\n      \"\"\"\r\n    And   a file named \"features/bad-parser-shouldnt-crash-process.feature\" with:\r\n      \"\"\"\r\n      Feature:\r\n        Scenario: after parsing fails in when, then step should be executed\r\n          When  a custom thing foo is created\r\n          Then  all is OK\r\n      \"\"\"\r\n    When I run \"behave -f plain \"features/bad-parser-shouldnt-crash-process.feature\"\"\r\n    Then it should fail with:\r\n      \"\"\"\r\n      0 scenarios passed, 1 failed, 0 skipped\r\n      0 steps passed, 1 failed, 1 skipped, 0 undefined\r\n      \"\"\"\r\n    And the command output should contain:\r\n        \"\"\"\r\n           Exception('could not parse text')\r\n        \"\"\"\r\n```\r\n\r\n## A possible solution idea\r\n\r\n```diff\r\ndiff --git a/behave/matchers.py b/behave/matchers.py\r\nindex 22beee8..868e633 100644\r\n--- a/behave/matchers.py\r\n+++ b/behave/matchers.py\r\n@@ -3,6 +3,7 @@ from __future__ import absolute_import, with_statement\r\n import re\r\n import parse\r\n from parse_type import cfparse\r\n+import six\r\n from behave import model\r\n\r\n\r\n@@ -72,7 +73,11 @@ class ParseMatcher(Matcher):\r\n         self.parser = parse.compile(self.string, self.custom_types)\r\n\r\n     def check_match(self, step):\r\n-        result = self.parser.parse(step)\r\n+        try:\r\n+            result = self.parser.parse(step)\r\n+        except CustomTypeParsingFailed:\r\n+            # TODO: need to print exception to stdout somehow\r\n+            return []\r\n         if not result:\r\n             return None\r\n\r\n@@ -96,6 +101,9 @@ class CFParseMatcher(ParseMatcher):\r\n         super(ParseMatcher, self).__init__(func, string, step_type)\r\n         self.parser = cfparse.Parser(self.string, self.custom_types)\r\n\r\n+class CustomTypeParsingFailed(Exception):\r\n+    pass\r\n+\r\n\r\n def register_type(**kw):\r\n     \"\"\"Registers a custom type that will be available to \"parse\"\r\n@@ -127,7 +135,18 @@ def register_type(**kw):\r\n         def step_impl(context, amount):\r\n             assert isinstance(amount, int)\r\n     \"\"\"\r\n-    ParseMatcher.custom_types.update(kw)\r\n+    def custom_type_converter(orig_converter, text):\r\n+        try:\r\n+            return orig_converter(text)\r\n+        except:\r\n+            # TODO: pass in the original exception (traceback) into the exception\r\n+            raise CustomTypeParsingFailed()\r\n+\r\n+    wrapped_converters = dict(\r\n+        (type_name, lambda text: custom_type_converter(type_converter, text))\r\n+        for (type_name, type_converter) in six.iteritems(kw)\r\n+    )\r\n+\r\n+    ParseMatcher.custom_types.update(wrapped_converters)\r\n```"},"comment":{"url":"https://api.github.com/repos/behave/behave/issues/comments/165915804","html_url":"https://github.com/behave/behave/issues/383#issuecomment-165915804","issue_url":"https://api.github.com/repos/behave/behave/issues/383","id":165915804,"user":{"login":"jenisys","id":371945,"avatar_url":"https://avatars.githubusercontent.com/u/371945?v=3","gravatar_id":"","url":"https://api.github.com/users/jenisys","html_url":"https://github.com/jenisys","followers_url":"https://api.github.com/users/jenisys/followers","following_url":"https://api.github.com/users/jenisys/following{/other_user}","gists_url":"https://api.github.com/users/jenisys/gists{/gist_id}","starred_url":"https://api.github.com/users/jenisys/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jenisys/subscriptions","organizations_url":"https://api.github.com/users/jenisys/orgs","repos_url":"https://api.github.com/users/jenisys/repos","events_url":"https://api.github.com/users/jenisys/events{/privacy}","received_events_url":"https://api.github.com/users/jenisys/received_events","type":"User","site_admin":false},"created_at":"2015-12-18T22:57:47Z","updated_at":"2015-12-18T22:57:47Z","body":"I will look a your solution, but my gut feeling is that the exception should be raised.\r\nWhen a type parsing fails (with an exception), the feature file contains invalid data (type value) in a step. Therefore, the step should fail (and you should be able to understand what went wrong).\r\n\r\nAfter using your example feature, I agree that this situation (errors/exceptions during type parsing) should be improved, but probably in the ``Step.run()`` method. Because there you can better provide the step context as information for the problem location."}}