protegrity-ai-developer-python 1.2.1__py3-none-any.whl

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 (53) hide show
  1. appython/__init__.py +12 -0
  2. appython/protector.py +554 -0
  3. appython/service/auth_provider.py +273 -0
  4. appython/service/auth_token_provider.py +45 -0
  5. appython/service/config.py +209 -0
  6. appython/service/payload_builder.py +141 -0
  7. appython/service/request_handler.py +115 -0
  8. appython/service/response_handler.py +78 -0
  9. appython/stats/__init__.py +3 -0
  10. appython/stats/collector.py +90 -0
  11. appython/stats/writer.py +185 -0
  12. appython/utils/codec_helper.py +86 -0
  13. appython/utils/constants.py +246 -0
  14. appython/utils/exceptions.py +141 -0
  15. appython/utils/input_preprocessor.py +325 -0
  16. appython/utils/output_postprocessor.py +99 -0
  17. protegrity_ai_developer_python-1.2.1.dist-info/METADATA +428 -0
  18. protegrity_ai_developer_python-1.2.1.dist-info/RECORD +53 -0
  19. protegrity_ai_developer_python-1.2.1.dist-info/WHEEL +5 -0
  20. protegrity_ai_developer_python-1.2.1.dist-info/entry_points.txt +2 -0
  21. protegrity_ai_developer_python-1.2.1.dist-info/licenses/LICENSE +21 -0
  22. protegrity_ai_developer_python-1.2.1.dist-info/top_level.txt +3 -0
  23. protegrity_developer_python/__init__.py +4 -0
  24. protegrity_developer_python/scan.py +37 -0
  25. protegrity_developer_python/securefind.py +83 -0
  26. protegrity_developer_python/utils/ccn_processing.py +59 -0
  27. protegrity_developer_python/utils/config.py +60 -0
  28. protegrity_developer_python/utils/constants.py +123 -0
  29. protegrity_developer_python/utils/discover.py +49 -0
  30. protegrity_developer_python/utils/logger.py +23 -0
  31. protegrity_developer_python/utils/pii_processing.py +291 -0
  32. protegrity_developer_python/utils/protector.py +23 -0
  33. protegrity_developer_python/utils/semantic_guardrails.py +240 -0
  34. protegrity_developer_python/utils/transform.py +66 -0
  35. pty_migrate/__init__.py +1 -0
  36. pty_migrate/check_cmd.py +871 -0
  37. pty_migrate/cli.py +93 -0
  38. pty_migrate/config.py +127 -0
  39. pty_migrate/create_policy_cmd.py +795 -0
  40. pty_migrate/payloads/__init__.py +51 -0
  41. pty_migrate/payloads/alphabets.json +42 -0
  42. pty_migrate/payloads/dataelements.json +342 -0
  43. pty_migrate/payloads/datastores.json +7 -0
  44. pty_migrate/payloads/deploy_policy_ta.json +1 -0
  45. pty_migrate/payloads/masks.json +18 -0
  46. pty_migrate/payloads/members.json +62 -0
  47. pty_migrate/payloads/policies.json +13 -0
  48. pty_migrate/payloads/roles.json +32 -0
  49. pty_migrate/payloads/rules.json +1639 -0
  50. pty_migrate/payloads/sources.json +10 -0
  51. pty_migrate/payloads/trusted_apps.json +8 -0
  52. pty_migrate/ppc_client.py +371 -0
  53. pty_migrate/stats_cmd.py +87 -0
appython/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ """
2
+ Protegrity Application Protector (AP) Python APIs
3
+ -------------------------------------------------
4
+ This module provides classes and methods to protect and unprotect
5
+ sensitive data using Protegrity's data protection mechanisms.
6
+ """
7
+
8
+ from appython.protector import Protector,Charset
9
+ from appython.utils.constants import CheckAccessType
10
+
11
+
12
+
appython/protector.py ADDED
@@ -0,0 +1,554 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ .. module:: protector
5
+ :synopsis: This module contains APIs for protect, unprotect, reprotect operations.
6
+ """
7
+ from enum import Enum
8
+ from datetime import datetime
9
+ import atexit
10
+ import os
11
+ from appython.utils.exceptions import (
12
+ ProtectError,
13
+ UnprotectError,
14
+ ReprotectError,
15
+ InvalidSessionError,
16
+ ProtectorError,
17
+ InitializationError,
18
+ )
19
+
20
+ from appython.utils.input_preprocessor import InputPreprocessor
21
+ from appython.service.payload_builder import PayloadBuilder
22
+ from appython.service.request_handler import RequestHandler
23
+ from appython.service.response_handler import ResponseHandler
24
+ from appython.service.auth_token_provider import AuthTokenProvider
25
+ from appython.service.config import load_config
26
+ from appython.service.auth_provider import create_auth_provider
27
+ from appython.stats.collector import UsageCollector
28
+ from appython.stats.writer import flush_stats
29
+
30
+
31
+ class Charset(Enum):
32
+ UTF8 = 2
33
+ UTF16LE = 4
34
+ UTF16BE = 5
35
+
36
+
37
+ class Protector(object):
38
+ """Protector class consists of all the APIs for protection operations."""
39
+
40
+ def create_session(self, policy_user, timeout=15, **kwargs):
41
+ """This method creates a new session with specified timeout value.
42
+
43
+ With valid Sessions you can perform the protection operations like
44
+ protect, unprotect, or reprotect.
45
+
46
+ Args:
47
+ policy_user (str): User name defined in the policy
48
+ timeout (int, optional): Session timeout, specified in minutes.
49
+ By default, the value of this parameter is set to 15.
50
+ **kwargs: Futuristic, currently no keyword arguments accepted.
51
+
52
+ Returns:
53
+ session: Object of the :class:`Session` class. This stores the user's current session
54
+ and provides all the Protection APIs.
55
+
56
+ Raises:
57
+ ProtectorError: If the policy user is passed null or empty.
58
+
59
+ Usage::
60
+
61
+ >>> from appython import Protector
62
+ >>> protector = Protector()
63
+ >>> session = protector.create_session("superuser")
64
+
65
+ """
66
+ if not policy_user:
67
+ raise ProtectorError(err_msg="-1, Policy user cannot be none or empty")
68
+ session = Session(policy_user, timeout, **kwargs)
69
+ return session
70
+
71
+ def get_version(self):
72
+ """Return the version of the AP Python in use.
73
+
74
+ The version number can be compared with that of the PEP Server package to ensure that both are the same.
75
+
76
+ Returns:
77
+ str: The Product version
78
+
79
+ Usage::
80
+
81
+ from appython import Protector
82
+ protector = Protector()
83
+ protector.get_version()
84
+
85
+ """
86
+ return "1.2.1"
87
+
88
+ def get_version_ex(self):
89
+ """Returns the extended version of the AP Python in use.
90
+
91
+ The extended version consists of AP-Python version number and Core Version number.
92
+ Core version number can be communicated to the Protegrity Support
93
+ while troubleshooting AP-Python related issues.
94
+
95
+ Returns:
96
+ str: The Product version and Core version
97
+
98
+ Usage::
99
+
100
+ from appython import Protector
101
+ protector = Protector()
102
+ protector.get_version_ex()
103
+
104
+ """
105
+ return "SDK Version: 1.2.1, Core Version: 1.2.1"
106
+
107
+ def terminate(self):
108
+ return True
109
+
110
+
111
+ class Session(object):
112
+ """Session class holds user session and provides Protection APIs.
113
+
114
+ A session object needs to be created first using create_session() API
115
+ of the Protector object. Using this object one can invoke
116
+ protection methods like protect, unprotect or reprotect on it.
117
+
118
+ Session object becomes invalid if remained idle for the specified
119
+ timeout period. Any operation with an invalid session leads to
120
+ exception.
121
+ """
122
+
123
+ def __init__(self, user, ttl, **kwargs):
124
+
125
+ try:
126
+ self._config = load_config()
127
+ self._auth_provider = create_auth_provider(self._config)
128
+ except InitializationError:
129
+ raise
130
+ except Exception as e:
131
+ raise InitializationError(err_msg=f"{e}")
132
+
133
+ # Keep legacy attributes for backward compat (tests may access them)
134
+ self.api_key = getattr(self._auth_provider, '_api_key', None)
135
+ self.jwt_token = getattr(self._auth_provider, '_jwt_token', None)
136
+
137
+ self._user = user
138
+ if ttl is None:
139
+ self._ttl = 15 * 60
140
+ elif not (isinstance(ttl, int) or isinstance(ttl, float)):
141
+ raise ValueError("timeout must be an integer or float value!!")
142
+ else:
143
+ self._ttl = int(ttl * 60) # in seconds
144
+ self._timestamp = datetime.now()
145
+ self._closed = False
146
+
147
+ # Usage statistics collector
148
+ self._stats = UsageCollector(user)
149
+
150
+ # Register atexit handler so stats flush before interpreter shutdown
151
+ atexit.register(self._atexit_flush)
152
+
153
+ def authenticate(self):
154
+ """Legacy authentication method. Kept for backward compatibility.
155
+
156
+ The new auth provider pattern handles authentication during __init__.
157
+ This method is no longer called internally but retained for any
158
+ external code that may reference it.
159
+
160
+ Returns:
161
+ tuple: (api_key, jwt_token)
162
+ """
163
+ email = os.environ.get("DEV_EDITION_EMAIL", None)
164
+ password = os.environ.get("DEV_EDITION_PASSWORD", None)
165
+ api_key = os.environ.get("DEV_EDITION_API_KEY", None)
166
+
167
+ if not email or not password:
168
+ raise InitializationError(
169
+ err_msg="Authentication failed: Both DEV_EDITION_EMAIL and DEV_EDITION_PASSWORD must be provided."
170
+ )
171
+
172
+ if not api_key:
173
+ raise InitializationError(
174
+ err_msg="Authentication failed: DEV_EDITION_API_KEY must be provided."
175
+ )
176
+ response = AuthTokenProvider.get_jwt_token(email, password, api_key)
177
+ if response.status_code != 200:
178
+ raise InitializationError(
179
+ err_msg=f"{response.json().get('error', 'Could not authenticate user.')}"
180
+ )
181
+ return api_key, response.json().get("jwt_token", None)
182
+
183
+ def __validate(self):
184
+ cur_time = datetime.now()
185
+ delta = (cur_time - self._timestamp).seconds
186
+ if delta >= self._ttl:
187
+ raise InvalidSessionError(err_msg="User session is invalid or timed out!!")
188
+ else:
189
+ self._timestamp = cur_time
190
+
191
+ def protect(self, data, de, **kwargs):
192
+ """Protect data using tokenization, data type preservation, no encryption,
193
+ or encryption data element.
194
+
195
+ It supports single as well as bulk protection without a maximum data limit.
196
+ However, you are recommended not to pass more than 1 MB of input data
197
+ for each protection call. For String and byte data types,
198
+ the maximum length for tokenization is 4096 bytes, while for
199
+ encryption no maximum length is defined.
200
+
201
+ It accepts data of following types:
202
+ - str
203
+ - bytes (future bytes)
204
+ - int
205
+ - long
206
+ - float
207
+ - datetime
208
+ - List (of same data types)
209
+ - Tuple (of same data types)
210
+
211
+ Args:
212
+ data (obj): Data to be protected. You can provide the data of above mentioned types.
213
+ de (str): String containing the data element name defined in policy.
214
+ **kwargs: Specify one or more of the following keyword arguments:
215
+ - external_iv: Specify the external initialization vector for
216
+ Tokenization and FPE protection methods.
217
+ - encrypt_to: Specify this argument for encrypting the data and set its value
218
+ to bytes. This argument is mandatory for encryption, except if you want
219
+ to encrypt byte data. This argument is not used for Tokenization and
220
+ FPE protection methods.
221
+ - external_tweak: Specify the external tweak value for FPE protection method.
222
+
223
+ Returns:
224
+ Protected data. In case of bulk, a tuple of output list(or tuple) and a tuple of
225
+ error codes is returned.
226
+
227
+ Raises:
228
+ ProtectError: This exception is thrown if the API is unable to protect the single data.
229
+ For Bulk, no exception is thrown.
230
+ InvalidSessionError : This exception is thrown if the session is invalid or has timed out.
231
+
232
+ Usage::
233
+
234
+ >>> from appython import Protector
235
+ >>> protector = Protector()
236
+ >>> session = protector.create_session("superuser")
237
+ >>> session.protect("Protegrity1", "name")
238
+ Pr9zdglWRy1
239
+
240
+ External IV
241
+ -----------
242
+ >>> session.protect("Protegrity1", "name", external_iv=bytes("1234"))
243
+ PrksvEshuy1
244
+
245
+ Bulk Call
246
+ ---------
247
+ >>> data = ["protegrity1234", "Protegrity1", "Protegrity56"]
248
+ >>> out, error_list = session.protect(data, "name")
249
+ >>> print("Protected Data: ")
250
+ >>> print(out)
251
+ >>> print("Error List: ")
252
+ >>> print(error_list)
253
+ Protected Data:
254
+ ['prMLJsM8fZUp34', 'Pr9zdglWRy1', 'Pra9Ez5LPG56']
255
+ Error List:
256
+ (6, 6, 6)
257
+
258
+ """
259
+ self.__validate()
260
+ try:
261
+ input = InputPreprocessor.convert_input_to_string(
262
+ data, kwargs, de, "protect"
263
+ )
264
+ arguments = InputPreprocessor.validate_parameters(
265
+ kwargs, input["input_datatype"], "protect", self._user, de
266
+ )
267
+ payload, return_type, base_url = PayloadBuilder.build_api_request(
268
+ input, arguments, "protect", self._config
269
+ )
270
+ response = RequestHandler.send_request(
271
+ payload, base_url, self._auth_provider, self._config
272
+ )
273
+ result = ResponseHandler.process(response, return_type, "protect")
274
+ self._stats.record_protect(de)
275
+ return result
276
+
277
+ except Exception as e:
278
+ raise ProtectError(err_msg=e)
279
+
280
+ def unprotect(self, data, de, **kwargs):
281
+ """Unprotect the protected data in its original form.
282
+
283
+ It supports both single as well bulk operations.
284
+
285
+ It accepts data of following types:
286
+ - str
287
+ - bytes (future bytes)
288
+ - int
289
+ - long
290
+ - float
291
+ - datetime
292
+ - List (of same data types)
293
+ - Tuple (of same data types)
294
+
295
+ Args:
296
+ data (obj): Data to be unprotected. You can provide the data of above mentioned types.
297
+ de (str): String containing the data element name defined in policy.
298
+ **kwargs: Specify one or more of the following keyword arguments:
299
+ - external_iv: Specify the external initialization vector for
300
+ Tokenization and FPE protection methods.
301
+ - decrypt_to: Specify this argument for decrypting the data and set its value
302
+ to the data type of the original data. For example, if you are unprotecting
303
+ a string data, then you must specify the output data type as str.
304
+ This argument is mandatory, except if you want to decrypt bytes data.
305
+ This argument is not used for Tokenization and FPE protection methods.
306
+ - external_tweak: Specify the external tweak value for FPE protection method.
307
+
308
+ Returns:
309
+ Unprotected data. In case of bulk, a tuple of output list(or tuple) and a tuple of
310
+ error codes is returned.
311
+
312
+ Raises:
313
+ UnprotectError: This exception is thrown if the API is unable to unprotect the single data.
314
+ For Bulk, no exception is thrown.
315
+ InvalidSessionError : This exception is thrown if the session is invalid or has timed out.
316
+
317
+ Usage::
318
+
319
+ >>> from appython import Protector
320
+ >>> protector = Protector()
321
+ >>> session = protector.create_session("superuser")
322
+ >>> output = session.protect("Protegrity1", "name")
323
+ >>> print("Protected Data: %s" %output)
324
+ >>> org = session.unprotect(output, "name")
325
+ >>> print("Unprotected Data: %s" %org)
326
+
327
+ Protected Data: Pr9zdglWRy1
328
+ Unprotected Data: Protegrity1
329
+
330
+ External IV
331
+ -----------
332
+ >>> output = session.protect("Protegrity1", "name", external_iv=bytes("1234"))
333
+ >>> print("Protected Data: %s" %output)
334
+ >>> org = session.unprotect(output, "name", external_iv=bytes("1234"))
335
+ >>> print("Unprotected Data: %s" %org)
336
+
337
+ Protected Data: PrksvEshuy1
338
+ Unprotected Data: Protegrity1
339
+
340
+ Bulk Call
341
+ ---------
342
+ >>> data = ["protegrity1234", "Protegrity1", "Protegrity56"]
343
+ >>> p_out = session.protect(data, "name")
344
+ >>> print("Protected Data: ")
345
+ >>> print(p_out)
346
+ >>> out = session.unprotect(p_out[0], "name")
347
+ >>> print("Unprotected Data: ")
348
+ >>> print(out)
349
+
350
+ Protected Data:
351
+ (['prMLJsM8fZUp34', 'Pr9zdglWRy1', 'Pra9Ez5LPG56'], (6, 6, 6))
352
+ Unprotected Data:
353
+ (['protegrity1234', 'Protegrity1', 'Protegrity56'], (8, 8, 8))
354
+ """
355
+ self.__validate()
356
+ try:
357
+ input = InputPreprocessor.convert_input_to_string(
358
+ data, kwargs, de, "unprotect"
359
+ )
360
+ arguments = InputPreprocessor.validate_parameters(
361
+ kwargs, input["input_datatype"], "unprotect", self._user, de
362
+ )
363
+ payload, return_type, base_url = PayloadBuilder.build_api_request(
364
+ input, arguments, "unprotect", self._config
365
+ )
366
+ response = RequestHandler.send_request(
367
+ payload, base_url, self._auth_provider, self._config
368
+ )
369
+ result = ResponseHandler.process(response, return_type, "unprotect")
370
+ self._stats.record_unprotect(de)
371
+ return result
372
+ except Exception as e:
373
+ raise UnprotectError(err_msg=e)
374
+
375
+ def reprotect(self, data, old_de, new_de, **kwargs):
376
+ """Reprotect data using tokenization, data type preservation, no encryption,
377
+ or encryption data element.
378
+
379
+ The protected data is first unprotected and then protected again with a new data element.
380
+ It supports bulk protection without a maximum data limit. However, you are recommended
381
+ not to pass more than 1 MB of input data for each protection call. Both old and
382
+ new data elements should be of the tokenization type.
383
+
384
+ For String and byte data types, the maximum length for tokenization is 4096 bytes,
385
+ while for encryption no maximum length is defined.
386
+
387
+ It accepts data of following types:
388
+ - str
389
+ - bytes (future bytes)
390
+ - int
391
+ - long
392
+ - float
393
+ - datetime
394
+ - List (of same data types)
395
+ - Tuple (of same data types)
396
+
397
+ Args:
398
+ data (str): Protected data to be reprotected. The data is first unprotected with the
399
+ old data element and then protected with the new data element.
400
+ old_de (str): String containing the data element name defined in the policy for the
401
+ input data. This data element is used to unprotect the protected data as part of
402
+ the reprotect operation.
403
+ new_de (str): String containing the data element name defined in the policy to create
404
+ the output data. This data element is used to protect the data as part of the
405
+ reprotect operation.
406
+ **kwargs: Specify one or more of the following keyword arguments:
407
+ - old_external_iv: Specify the external initialization vectors for Tokenization
408
+ and FPE protection methods. These arguments are Optional.
409
+ - new_external_iv: Specify the external initialization vectors for Tokenization
410
+ and FPE protection methods. These arguments are Optional.
411
+ - old_external_tweak: Specify the external tweak values for FPE protection method.
412
+ These arguments are Optional.
413
+ - new_external_tweak: Specify the external tweak values for FPE protection method.
414
+ These arguments are Optional.
415
+
416
+ Returns:
417
+ Reprotected data. In case of bulk, a tuple of output list(or tuple) and a tuple of
418
+ error codes is returned.
419
+
420
+ Raises:
421
+ ReprotectError: This exception is thrown if the API is unable to reprotect the single data.
422
+ For Bulk, no exception is thrown.
423
+ InvalidSessionError: This exception is thrown if the session is invalid or has timed out.
424
+
425
+ Usage::
426
+
427
+ >>> from appython import Protector
428
+ >>> protector = Protector()
429
+ >>> session = protector.create_session("superuser")
430
+ >>> output = session.protect("Protegrity1", "name")
431
+ >>> print("Protected Data: %s" %output)
432
+ >>> r_out = session.reprotect(output, "name", "SUCCESS_REPROTECT_STR")
433
+ >>> print("Reprotected Data: %s" %r_out)
434
+
435
+ Protected Data: Pr9zdglWRy1
436
+ Reprotected Data: 7gD6aY1Aja9
437
+
438
+ External IV
439
+ -----------
440
+ >>> p_out = session.protect("Protegrity1", "name",
441
+ >>> external_iv=bytes("1234"))
442
+ >>> print("Protected Data: %s" %p_out)
443
+ >>> r_out = session.reprotect(p_out, "name",
444
+ "SUCCESS_REPROTECT_STR", old_external_iv=bytes("1234"),
445
+ >>> new_external_iv=bytes("123456"))
446
+ >>> print("Reprotected Data: %s" %r_out)
447
+
448
+ Protected Data: PrksvEshuy1
449
+ Reprotected Data: PrKxfmdTGy1
450
+
451
+ Bulk Call
452
+ ---------
453
+ >>> data = ["protegrity1234", "Protegrity1", "Protegrity56"]
454
+ >>> p_out = session.protect(data, "name")
455
+ >>> print("Protected Data: ")
456
+ >>> print(p_out)
457
+ >>> r_out = session.reprotect(p_out[0], "name", "SUCCESS_REPROTECT_STR")
458
+ >>> print("Reprotected Data: ")
459
+ >>> print(r_out)
460
+
461
+ Protected Data:
462
+ (['pLAvXYIAbp5234', 'P8PCmC8gty1', 'PHNjXrw7Iy56'], (6, 6, 6))
463
+ Reprotected Data:
464
+ (['prMLJsM8fZUp34', 'Pr9zdglWRy1', 'Pra9Ez5LPG56'], (6, 6, 6))
465
+ """
466
+ self.__validate()
467
+ try:
468
+ input = InputPreprocessor.convert_input_to_string(
469
+ data, kwargs, new_de, "reprotect"
470
+ )
471
+ arguments = InputPreprocessor.validate_parameters(
472
+ kwargs, input["input_datatype"], "reprotect", self._user, old_de, new_de
473
+ )
474
+ payload, return_type, base_url = PayloadBuilder.build_api_request(
475
+ input, arguments, "reprotect", self._config
476
+ )
477
+ response = RequestHandler.send_request(
478
+ payload, base_url, self._auth_provider, self._config
479
+ )
480
+ result = ResponseHandler.process(response, return_type, "reprotect")
481
+ self._stats.record_reprotect(old_de, new_de)
482
+ return result
483
+ except Exception as e:
484
+ raise ReprotectError(err_msg=e)
485
+
486
+ def flush_audits(self):
487
+ self.__validate()
488
+ return True
489
+
490
+ def check_access(self, de, access_type, newde=None):
491
+ """Return access permission status of the user for a specified data element.
492
+
493
+ Args:
494
+ de (str): String containing the data element name defined in policy.
495
+ access_type (CheckAccessType): Type of the access of the user for the data element.
496
+ You can specify a value for this parameter from the CheckAccessType enumeration.
497
+ - CheckAccessType.PROTECT
498
+ - CheckAccessType.UNPROTECT
499
+ - CheckAccessType.REPROTECT
500
+
501
+ Returns:
502
+ bool: True if the user has access, else False.
503
+
504
+ Raises:
505
+ InvalidSessionError: This exception is thrown if the session is invalid or has timed out.
506
+ PyCoreProviderError: This exception is thrown if unable to check access.
507
+
508
+ Usage::
509
+
510
+ >>> from appython import Protector
511
+ >>> from appython import CheckAccessType
512
+ >>> protector = Protector()
513
+ >>> session = protector.create_session("User1")
514
+ >>> print(session.check_access("AlphaNumeric", CheckAccessType.REPROTECT))
515
+ True
516
+
517
+ """
518
+ self.__validate()
519
+ return True
520
+
521
+ def _atexit_flush(self):
522
+ """Called by atexit — flushes stats while modules are still alive."""
523
+ if not self._closed and hasattr(self, "_stats") and self._stats.enabled:
524
+ flush_stats(self._stats.get_session_data())
525
+ self._closed = True
526
+
527
+ def __close_session(self):
528
+ # Flush usage stats to disk before closing
529
+ if hasattr(self, "_stats") and self._stats.enabled:
530
+ flush_stats(self._stats.get_session_data())
531
+ # Unregister atexit since we've already flushed
532
+ atexit.unregister(self._atexit_flush)
533
+ self._provider = None
534
+ self._timestamp = None
535
+ self._ttl = None
536
+ self._user = None
537
+ self._closed = True
538
+
539
+ def __repr__(self):
540
+ return "Session(user=%s)" % (self._user)
541
+
542
+ def __enter__(self):
543
+ return self
544
+
545
+ def __exit__(self, *args):
546
+ self.flush_audits()
547
+ self.__close_session()
548
+
549
+ def __del__(self):
550
+ try:
551
+ if hasattr(self, "_closed") and not self._closed:
552
+ self.__close_session()
553
+ except Exception:
554
+ pass