t-bug-catcher 0.5.5__tar.gz → 0.5.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/PKG-INFO +1 -1
  2. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/setup.cfg +1 -1
  3. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/setup.py +1 -1
  4. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/__init__.py +3 -1
  5. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/bug_catcher.py +10 -0
  6. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/bug_snag.py +0 -2
  7. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/jira.py +30 -18
  8. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher.egg-info/PKG-INFO +1 -1
  9. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher.egg-info/SOURCES.txt +0 -1
  10. t_bug_catcher-0.5.5/t_bug_catcher/validation.py +0 -123
  11. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/MANIFEST.in +0 -0
  12. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/README.rst +0 -0
  13. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/pyproject.toml +0 -0
  14. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/requirements.txt +0 -0
  15. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/config.py +0 -0
  16. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/exceptions.py +0 -0
  17. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/resources/whispers_config.yml +0 -0
  18. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/stack_saver.py +0 -0
  19. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/utils/__init__.py +0 -0
  20. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/utils/common.py +0 -0
  21. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/utils/logger.py +0 -0
  22. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher/workitems.py +0 -0
  23. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher.egg-info/dependency_links.txt +0 -0
  24. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher.egg-info/not-zip-safe +0 -0
  25. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher.egg-info/requires.txt +0 -0
  26. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/t_bug_catcher.egg-info/top_level.txt +0 -0
  27. {t_bug_catcher-0.5.5 → t_bug_catcher-0.5.7}/tests/test_t_bug_catcher.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: t_bug_catcher
3
- Version: 0.5.5
3
+ Version: 0.5.7
4
4
  Summary: Bug catcher
5
5
  Home-page: https://www.thoughtful.ai/
6
6
  Author: Thoughtful
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 0.5.5
2
+ current_version = 0.5.7
3
3
  commit = True
4
4
  tag = False
5
5
 
@@ -26,7 +26,7 @@ setup(
26
26
  packages=find_packages(include=["t_bug_catcher", "t_bug_catcher.*"]),
27
27
  test_suite="tests",
28
28
  url="https://www.thoughtful.ai/",
29
- version="0.5.5",
29
+ version="0.5.7",
30
30
  zip_safe=False,
31
31
  install_requires=install_requirements,
32
32
  include_package_data=True,
@@ -3,7 +3,7 @@
3
3
  __author__ = """Thoughtful"""
4
4
  __email__ = "support@thoughtful.ai"
5
5
  # fmt: off
6
- __version__ = '0.5.5'
6
+ __version__ = '0.5.7'
7
7
  # fmt: on
8
8
 
9
9
  from .bug_catcher import (
@@ -12,6 +12,7 @@ from .bug_catcher import (
12
12
  attach_file_to_exception,
13
13
  install_sys_hook,
14
14
  uninstall_sys_hook,
15
+ get_errors_count,
15
16
  )
16
17
 
17
18
  __all__ = [
@@ -20,4 +21,5 @@ __all__ = [
20
21
  "attach_file_to_exception",
21
22
  "install_sys_hook",
22
23
  "uninstall_sys_hook",
24
+ "get_errors_count",
23
25
  ]
@@ -92,12 +92,17 @@ class BugCatcher:
92
92
  self.__configurator: Configurator = Configurator(self.__jira, self.__bug_snag)
93
93
  self.__sys_excepthook = None
94
94
  self.__stack_saver = StackSaver()
95
+ self.__errors_count = 0
95
96
 
96
97
  @property
97
98
  def configure(self):
98
99
  """Configures the JiraPoster and BugSnag classes."""
99
100
  return self.__configurator
100
101
 
102
+ def get_errors_count(self):
103
+ """Returns the number of exceptions reported."""
104
+ return self.__errors_count
105
+
101
106
  def report_error(
102
107
  self,
103
108
  exception: Optional[Exception] = None,
@@ -186,6 +191,8 @@ class BugCatcher:
186
191
  exc_info = f"{os.path.basename(frames[-1].filename)}:{frames[-1].name}:{frames[-1].lineno}"
187
192
  exception.handled_error = exc_info
188
193
 
194
+ self.__errors_count += 1
195
+
189
196
  def report_error_to_jira(
190
197
  self,
191
198
  exception: Optional[Exception] = None,
@@ -286,6 +293,8 @@ class BugCatcher:
286
293
  if self.__configurator.is_bugsnag_configured:
287
294
  self.__bug_snag.report_unhandled_error(exc_type, exc_value, exc_traceback)
288
295
 
296
+ self.__errors_count += 1
297
+
289
298
  def __get_sys_hook_attribute(self, attribute: str = "bug_catcher_client"):
290
299
  """Checks if the system hook is installed.
291
300
 
@@ -332,3 +341,4 @@ report_error_to_jira = __bug_catcher.report_error_to_jira
332
341
  report_error_to_bugsnag = __bug_catcher.report_error_to_bugsnag
333
342
  install_sys_hook = __bug_catcher.install_sys_hook
334
343
  uninstall_sys_hook = __bug_catcher.uninstall_sys_hook
344
+ get_errors_count = __bug_catcher.get_errors_count
@@ -1,5 +1,4 @@
1
1
  import sys
2
- from datetime import datetime
3
2
  from typing import Optional
4
3
 
5
4
  import bugsnag
@@ -44,7 +43,6 @@ class BugSnag:
44
43
  "Content-Type": "application/json",
45
44
  "Bugsnag-Api-Key": api_key,
46
45
  "Bugsnag-Payload-Version": "4",
47
- "Bugsnag-Sent-At": f"{datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')}",
48
46
  "Bugsnag-Span-Sampling": "True",
49
47
  },
50
48
  data='{"message": "test"}',
@@ -476,11 +476,32 @@ class Jira:
476
476
  return []
477
477
  frames = get_frames(exc_traceback)
478
478
  file_name, line_no, _, _ = frames[-1]
479
- path = Path.relative_to(Path(file_name), Path.cwd())
480
- code_line = (
481
- f"{self._build_info['repository_url']}/src/{self._build_info['last_commit']}"
482
- f"/{path.as_posix()}?at={self._build_info['branch']}#lines-{line_no}"
483
- )
479
+ try:
480
+ path = Path.relative_to(Path(file_name), Path.cwd())
481
+ code_line = (
482
+ f"{self._build_info['repository_url']}/src/{self._build_info['last_commit']}"
483
+ f"/{path.as_posix()}?at={self._build_info['branch']}#lines-{line_no}"
484
+ )
485
+ payload = [
486
+ {
487
+ "type": "text",
488
+ "text": f"{str(path.as_posix())}:{line_no}",
489
+ "marks": [
490
+ {
491
+ "type": "link",
492
+ "attrs": {"href": code_line},
493
+ }
494
+ ],
495
+ },
496
+ ]
497
+ except ValueError:
498
+ payload = [
499
+ {
500
+ "type": "text",
501
+ "text": "Package error.",
502
+ },
503
+ ]
504
+
484
505
  input_datetime = datetime.strptime(self._build_info["commit_datetime"], "%Y-%m-%d %H:%M:%S").strftime(
485
506
  "%d %B %Y %I:%M:%S %p"
486
507
  )
@@ -521,17 +542,8 @@ class Jira:
521
542
  "type": "text",
522
543
  "text": " > ",
523
544
  },
524
- {
525
- "type": "text",
526
- "text": f"{str(path.as_posix())}:{line_no}",
527
- "marks": [
528
- {
529
- "type": "link",
530
- "attrs": {"href": code_line},
531
- }
532
- ],
533
- },
534
- ],
545
+ ]
546
+ + payload,
535
547
  },
536
548
  {
537
549
  "type": "paragraph",
@@ -1375,7 +1387,7 @@ class Jira:
1375
1387
  existing_ticket=existing_ticket,
1376
1388
  summary=summary,
1377
1389
  )
1378
- if os.path.exists(stack_trace):
1390
+ if stack_trace and os.path.exists(stack_trace):
1379
1391
  os.remove(stack_trace)
1380
1392
  return existing_ticket
1381
1393
 
@@ -1405,7 +1417,7 @@ class Jira:
1405
1417
  labels=["bug_catcher", "fatal_error"],
1406
1418
  priority=priority,
1407
1419
  )
1408
- if os.path.exists(stack_trace):
1420
+ if stack_trace and os.path.exists(stack_trace):
1409
1421
  os.remove(stack_trace)
1410
1422
  return response
1411
1423
  except Exception as ex:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: t_bug_catcher
3
- Version: 0.5.5
3
+ Version: 0.5.7
4
4
  Summary: Bug catcher
5
5
  Home-page: https://www.thoughtful.ai/
6
6
  Author: Thoughtful
@@ -11,7 +11,6 @@ t_bug_catcher/config.py
11
11
  t_bug_catcher/exceptions.py
12
12
  t_bug_catcher/jira.py
13
13
  t_bug_catcher/stack_saver.py
14
- t_bug_catcher/validation.py
15
14
  t_bug_catcher/workitems.py
16
15
  t_bug_catcher.egg-info/PKG-INFO
17
16
  t_bug_catcher.egg-info/SOURCES.txt
@@ -1,123 +0,0 @@
1
- import ast
2
- import logging
3
-
4
- from t_bug_catcher.utils.common import strip_path
5
-
6
- validation_logger = logging.getLogger("t_bug_catcher")
7
-
8
- validation_logger.setLevel(logging.DEBUG)
9
- console_handler = logging.StreamHandler()
10
- console_handler.setLevel(logging.DEBUG)
11
-
12
- formatter = logging.Formatter("%(levelname)s - %(name)s - %(message)s")
13
- validation_logger.addHandler(console_handler)
14
-
15
-
16
- class IncorrectTryBlockVisitor(ast.NodeVisitor):
17
- """A visitor that checks for incorrect try-except blocks."""
18
-
19
- def __init__(self):
20
- """Initializes the IncorrectTryBlockVisitor class."""
21
- self.errors = {}
22
-
23
- def visit_Try(self, node):
24
- """Visits the try-except block in the AST."""
25
- message = (
26
- "Error not handled: specify the error type or re-raise exception or "
27
- "report it with `t_bug_catcher.report_error()`"
28
- )
29
- for handler in node.handlers:
30
- if isinstance(handler.type, ast.Name) and handler.type.id == "Exception":
31
- if not self._contains_raise(handler.body) and not self._contains_correct_error_handling(handler.body):
32
- self.errors[handler.lineno] = message
33
- elif handler.type is None: # 'except:' that catches everything
34
- if not any(isinstance(x, ast.Raise) for x in handler.body):
35
- self.errors[handler.lineno] = message
36
- self.generic_visit(node)
37
-
38
- @staticmethod
39
- def _contains_correct_error_handling(statements):
40
- for stmt in statements:
41
- if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
42
- func = stmt.value.func
43
- if (isinstance(func, ast.Name) and func.id == "report_error") or (
44
- isinstance(func, ast.Attribute) and func.attr == "report_error"
45
- ):
46
- return True
47
- return False
48
-
49
- @staticmethod
50
- def _contains_raise(statements):
51
- return any(isinstance(stmt, ast.Raise) for stmt in statements)
52
-
53
- def get_errors(self):
54
- """Returns the errors found in the try-except blocks."""
55
- return self.errors
56
-
57
-
58
- class ValidationWarning:
59
- """Base class to represent any type of validation warning."""
60
-
61
- def __init__(self, file_path: str, lineno: int, message: str, source_code: str):
62
- """Initializes the ValidationWarning class."""
63
- self.lineno = lineno
64
- self.message = message
65
- self.__source_code_lines = source_code.split("\n")
66
- self.code_line = self.__source_code_lines[lineno - 1]
67
- self.code_lines = "\n".join(self.__source_code_lines[lineno - 3 : lineno + 2])
68
- self.file_path = strip_path(file_path)
69
-
70
-
71
- class BroadExceptionWarning(ValidationWarning):
72
- """A class to represent a broad exception warning."""
73
-
74
- def __init__(self, file_path: str, lineno: int, message: str, source_code: str):
75
- """Initializes the BroadExceptionWarning class."""
76
- super().__init__(file_path, lineno, message, source_code)
77
- self.warning_code = "TBC002"
78
-
79
-
80
- class ConfigWarning(ValidationWarning):
81
- """A class to represent a config warning."""
82
-
83
- def __init__(self, file_path: str, lineno: int, message: str, source_code: str):
84
- """Initializes the ConfigWarning class."""
85
- super().__init__(file_path, lineno, message, source_code)
86
- self.warning_code = "TBC001"
87
-
88
-
89
- class PreRunValidation:
90
- """A class to perform pre-run validation checks."""
91
-
92
- def __init__(self):
93
- """Initializes the PreRunValidation class."""
94
- self.warnings: list[ValidationWarning] = []
95
-
96
- def check_broad_exceptions(self, filename):
97
- """Checks for broad exception warnings in the specified file."""
98
- with open(filename, "r", encoding="utf8") as source:
99
- source_code = source.read()
100
-
101
- try:
102
- tree = ast.parse(source_code, filename=filename)
103
- except SyntaxError:
104
- return
105
- except Exception as ex:
106
- validation_logger.error(f"Unable to validate: {filename} - {ex}")
107
- return
108
-
109
- visitor = IncorrectTryBlockVisitor()
110
- visitor.visit(tree)
111
-
112
- for line, message in visitor.get_errors().items():
113
- be_warn = BroadExceptionWarning(filename, line, message, source_code=source_code)
114
- self.warnings.append(be_warn)
115
-
116
- def check_configuration_exceptions(self, filename):
117
- """Checks for configurations warnings in the specified file."""
118
- pass
119
-
120
- def validate_file(self, filename):
121
- """Checks for warnings in the specified file."""
122
- self.check_broad_exceptions(filename)
123
- self.check_configuration_exceptions(filename)
File without changes
File without changes