22import uuid
33import random
44import atexit
5-
6- from .utils import Dsn , SkipEvent , ContextVar
5+ import weakref
6+ from datetime import datetime
7+
8+ from ._compat import string_types
9+ from .utils import (
10+ strip_event ,
11+ flatten_metadata ,
12+ convert_types ,
13+ handle_in_app ,
14+ get_type_name ,
15+ pop_hidden_keys ,
16+ Dsn ,
17+ )
718from .transport import Transport
819from .consts import DEFAULT_OPTIONS , SDK_INFO
9- from .event import strip_event , flatten_metadata , convert_types , Event
1020
1121
1222NO_DSN = object ()
1323
14- _most_recent_exception = ContextVar ("sentry_most_recent_exception" )
1524
25+ def get_options (* args , ** kwargs ):
26+ if args and (isinstance (args [0 ], string_types ) or args [0 ] is None ):
27+ dsn = args [0 ]
28+ args = args [1 :]
29+ else :
30+ dsn = None
31+
32+ rv = dict (DEFAULT_OPTIONS )
33+ options = dict (* args , ** kwargs )
34+ if dsn is not None and options .get ("dsn" ) is None :
35+ options ["dsn" ] = dsn
36+
37+ for key , value in options .items ():
38+ if key not in rv :
39+ raise TypeError ("Unknown option %r" % (key ,))
40+ rv [key ] = value
1641
17- def _get_default_integrations ():
18- from .integrations .logging import LoggingIntegration
19- from .integrations .excepthook import ExcepthookIntegration
42+ if rv ["dsn" ] is None :
43+ rv ["dsn" ] = os .environ .get ("SENTRY_DSN" )
2044
21- yield LoggingIntegration
22- yield ExcepthookIntegration
45+ return rv
2346
2447
2548class Client (object ):
26- def __init__ (self , dsn = None , * args , ** kwargs ):
27- passed_dsn = dsn
28- if dsn is NO_DSN :
29- dsn = None
30- else :
31- if dsn is None :
32- dsn = os .environ .get ("SENTRY_DSN" )
33- if not dsn :
34- dsn = None
35- else :
36- dsn = Dsn (dsn )
37- options = dict (DEFAULT_OPTIONS )
38- options .update (* args , ** kwargs )
49+ def __init__ (self , * args , ** kwargs ):
50+ options = get_options (* args , ** kwargs )
51+
52+ dsn = options ["dsn" ]
53+ if dsn is not None :
54+ dsn = Dsn (dsn )
55+
3956 self .options = options
4057 self ._transport = self .options .pop ("transport" )
4158 if self ._transport is None and dsn is not None :
@@ -45,18 +62,6 @@ def __init__(self, dsn=None, *args, **kwargs):
4562 https_proxy = self .options .pop ("https_proxy" ),
4663 )
4764 self ._transport .start ()
48- elif passed_dsn is not None and self ._transport is not None :
49- raise ValueError ("Cannot pass DSN and a custom transport." )
50-
51- integrations = list (options .pop ("integrations" ) or ())
52-
53- if options ["default_integrations" ]:
54- for cls in _get_default_integrations ():
55- if not any (isinstance (x , cls ) for x in integrations ):
56- integrations .append (cls ())
57-
58- for integration in integrations :
59- integration (self )
6065
6166 request_bodies = ("always" , "never" , "small" , "medium" )
6267 if options ["request_bodies" ] not in request_bodies :
@@ -66,6 +71,8 @@ def __init__(self, dsn=None, *args, **kwargs):
6671 )
6772 )
6873
74+ self ._exceptions_seen = weakref .WeakKeyDictionary ()
75+
6976 atexit .register (self .close )
7077
7178 @property
@@ -80,11 +87,13 @@ def disabled(cls):
8087 return cls (NO_DSN )
8188
8289 def _prepare_event (self , event , scope ):
83- if event .get ("event_id " ) is None :
84- event ["event_id " ] = uuid . uuid4 (). hex
90+ if event .get ("timestamp " ) is None :
91+ event ["timestamp " ] = datetime . utcnow ()
8592
8693 if scope is not None :
87- scope .apply_to_event (event )
94+ event = scope .apply_to_event (event )
95+ if event is None :
96+ return
8897
8998 for key in "release" , "environment" , "server_name" , "repos" , "dist" :
9099 if event .get (key ) is None :
@@ -95,41 +104,67 @@ def _prepare_event(self, event, scope):
95104 if event .get ("platform" ) is None :
96105 event ["platform" ] = "python"
97106
107+ event = handle_in_app (
108+ event , self .options ["in_app_exclude" ], self .options ["in_app_include" ]
109+ )
98110 event = strip_event (event )
99- event = flatten_metadata (event )
100- event = convert_types (event )
111+
112+ before_send = self .options ["before_send" ]
113+ if before_send is not None :
114+ event = before_send (event )
115+
116+ if event is not None :
117+ pop_hidden_keys (event )
118+ event = flatten_metadata (event )
119+ event = convert_types (event )
120+
101121 return event
102122
103- def _check_should_capture (self , event ):
123+ def _is_ignored_error (self , event ):
124+ exc_info = event .get ("__sentry_exc_info" )
125+
126+ if not exc_info or exc_info [0 ] is None :
127+ return False
128+
129+ type_name = get_type_name (exc_info [0 ])
130+ full_name = "%s.%s" % (exc_info [0 ].__module__ , type_name )
131+
132+ for errcls in self .options ["ignore_errors" ]:
133+ # String types are matched against the type name in the
134+ # exception only
135+ if isinstance (errcls , string_types ):
136+ if errcls == full_name or errcls == type_name :
137+ return True
138+ else :
139+ if issubclass (exc_info [0 ], errcls ):
140+ return True
141+
142+ return False
143+
144+ def _should_capture (self , event , scope = None ):
104145 if (
105146 self .options ["sample_rate" ] < 1.0
106147 and random .random () >= self .options ["sample_rate" ]
107148 ):
108- raise SkipEvent ()
149+ return False
109150
110- if event ._exc_value is not None :
111- exclusions = self .options ["ignore_errors" ]
112- exc_type = type (event ._exc_value )
151+ if self ._is_ignored_error (event ):
152+ return False
113153
114- if any (issubclass (exc_type , e ) for e in exclusions ):
115- raise SkipEvent ()
116-
117- if _most_recent_exception .get (None ) is event ._exc_value :
118- raise SkipEvent ()
119- _most_recent_exception .set (event ._exc_value )
154+ return True
120155
121156 def capture_event (self , event , scope = None ):
122157 """Captures an event."""
123158 if self ._transport is None :
124159 return
125- if not isinstance ( event , Event ):
126- event = Event ( event )
127- try :
128- self ._check_should_capture (event )
160+ rv = event . get ( "event_id" )
161+ if rv is None :
162+ event [ "event_id" ] = rv = uuid . uuid4 (). hex
163+ if self ._should_capture (event , scope ):
129164 event = self ._prepare_event (event , scope )
130- except SkipEvent :
131- return
132- self . _transport . capture_event ( event )
165+ if event is not None :
166+ self . _transport . capture_event ( event )
167+ return True
133168
134169 def drain_events (self , timeout = None ):
135170 if timeout is None :
0 commit comments