[
  {
    "path": ".flake8",
    "content": "[flake8]\nmax-line-length = 100\nmax-complexity = 10\nexclude = test/*\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "## Description of the change\n\n> Please include a summary of the change and which issues are fixed.\n> Please also include relevant motivation and context.\n\n## Type of change\n\n- [ ] Bug fix (non-breaking change that fixes an issue)\n- [ ] New feature (non-breaking change that adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)\n- [ ] Maintenance\n- [ ] New release\n\n## Related issues\n\n> Shortcut stories and GitHub issues (delete irrelevant)\n\n- Fix [SC-]\n- Fix #1\n\n## Checklists\n\n### Development\n\n- [ ] Lint rules pass locally\n- [ ] The code changed/added as part of this pull request has been covered with tests\n- [ ] All tests related to the changed code pass in development\n\n### Code review\n\n- [ ] This pull request has a descriptive title and information useful to a reviewer. There may be a screenshot or screencast attached\n- [ ] \"Ready for review\" label attached to the PR and reviewers assigned\n- [ ] Issue from task tracker has a link to this pull request\n- [ ] Changes have been reviewed by at least one other engineer\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Pyrollbar CI\n\non:\n  push:\n    branches: [ master ]\n    tags: [ v* ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [3.9, '3.10', 3.11, 3.12, 3.13, 3.14]\n        framework:\n          - NONE\n          - FLASK_VERSION=2.3.3\n          - FLASK_VERSION=3.1.2\n          - DJANGO_VERSION=4.2.25\n          - DJANGO_VERSION=5.2.7\n          - PYRAMID_VERSION=1.10.8\n          - PYRAMID_VERSION=2.0.2\n          - FASTAPI_VERSION=0.115.1 httpx==0.27.2 python-multipart==0.0.12\n          - FASTAPI_VERSION=0.118.3 httpx==0.28.1 python-multipart==0.0.20\n        exclude:\n          # Test frameworks on the python versions they support, according to pypi registry\n\n          # Django\n          - framework: DJANGO_VERSION=5.2.7\n            python-version: 3.9\n\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          submodules: recursive\n\n      - name: Setup Python\n        uses: actions/setup-python@v4\n        with:\n          python-version: ${{ matrix.python-version }}\n\n      - name: Install package with test dependencies\n        run: pip install --group test .\n\n      - name: Set the framework\n        if: ${{ matrix.framework != 'NONE' }}\n        run: echo ${{ matrix.framework }} >> $GITHUB_ENV\n\n      - name: Install Flask\n        if: ${{ contains(matrix.framework, 'FLASK_VERSION') }}\n        run: pip install Flask==$FLASK_VERSION\n\n      - name: Install Twisted\n        if: ${{ contains(matrix.framework, 'TWISTED_VERSION') }}\n        run: pip install Twisted==$TWISTED_VERSION idna==2.10\n\n      - name: Install Django\n        if: ${{ contains(matrix.framework, 'DJANGO_VERSION') }}\n        run: pip install Django==$DJANGO_VERSION\n\n      - name: Install Pyramid\n        if: ${{ contains(matrix.framework, 'PYRAMID_VERSION') }}\n        run: pip install pyramid==$PYRAMID_VERSION\n\n      - name: Install Starlette\n        if: ${{ contains(matrix.framework, 'STARLETTE_VERSION') }}\n        run: pip install starlette==$STARLETTE_VERSION\n\n      - name: Install FastAPI\n        if: ${{ contains(matrix.framework, 'FASTAPI_VERSION') }}\n        run: pip install fastapi==$FASTAPI_VERSION\n\n      - name: Run tests\n        run: pytest\n"
  },
  {
    "path": ".gitignore",
    "content": "*.pyc\n*.swp\n*.swo\ndist/\nbuild/\nrollbar.egg-info/\n*.egg\n.eggs/\n.idea/\n*~\nPipfile\nPipfile.lock\n.pytest_cache/\n.python-version\n.tox/\n/venv*\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\nThe change log is also available on the [GitHub Releases Page](https://github.com/rollbar/pyrollbar/releases).\n\n**1.4.0-beta**\n\n- Added support for Python 3.14 by @danielmorell in [#484](https://github.com/rollbar/pyrollbar/pull/484)\n- Added support for error correlation headers. by @danielmorell in [#491](https://github.com/rollbar/pyrollbar/pull/491)\n\n**1.3.0**\n\n- Added support for classes to define the method `__rollbar_repr__` to control how objects are serialized by @danielmorell in [#479](https://github.com/rollbar/pyrollbar/pull/479)\n- Added support for Python 3.13 by @danielmorell in [#477](https://github.com/rollbar/pyrollbar/pull/477)\n- Removed support for Python 3.6 by @danielmorell in [#480](https://github.com/rollbar/pyrollbar/pull/480)\n- Updated framework versions tested against by @danielmorell in [#480](https://github.com/rollbar/pyrollbar/pull/480)\n\n**1.2.0**\n\n- Added support for custom payload transforms by @danielmorell in [#470](https://github.com/rollbar/pyrollbar/pull/470)\n- Added custom data to the message body by @waltjones in [#473](https://github.com/rollbar/pyrollbar/pull/473)\n- Added support for host override setting by @danielmorell in [#468](https://github.com/rollbar/pyrollbar/pull/468)\n- Fixed `RollbarHandler` reconfigures root logger by @ilkecan in [#463](https://github.com/rollbar/pyrollbar/pull/463)\n- Fixed `include_request_body` setting not checked for various frameworks by @danielmorell in [#469](https://github.com/rollbar/pyrollbar/pull/469)\n- Fixed `namedtuple` fields not being scrubbed by @danielmorell in [#474](https://github.com/rollbar/pyrollbar/pull/474)\n\n**1.1.2**\n\nFixed build missing entrypoints for pyramid and cli in [#471](https://github.com/rollbar/pyrollbar/pull/471)\n\n**1.1.1**\n\nFixed #465 httpx 0.28.0 not compatible with pyrollbar by @danielmorell in [#466](https://github.com/rollbar/pyrollbar/pull/466)\n\n**1.1.0**\n\n- Updated supported/tested frameworks and modernized tests and packaging by @danielmorell in [#455](https://github.com/rollbar/pyrollbar/pull/455)\n- Fixed #398 FastAPI integration fails if docs are disabled by @danielmorell in [#459](https://github.com/rollbar/pyrollbar/pull/459)\n- Support `pathlib.Path()` objects by @singingwolfboy in [$450](https://github.com/rollbar/pyrollbar/pull/450)\n- Added support for Python 3.12 by @danielmorell in [#460](https://github.com/rollbar/pyrollbar/pull/460)\n- Changed the `ShortenerTransform` to use breadth first traversal by @danielmorell in [#461](https://github.com/rollbar/pyrollbar/pull/461)\n- Fixed shortener multi level shortening by @danielmorell and @pawelsz-rb. See [#449](https://github.com/rollbar/pyrollbar/pull/449)\n\n**1.1.0-beta**\n\n- Updated supported/tested frameworks and modernized tests and packaging by @danielmorell in [#455](https://github.com/rollbar/pyrollbar/pull/455)\n- Fixed #398 FastAPI integration fails if docs are disabled by @danielmorell in [#459](https://github.com/rollbar/pyrollbar/pull/459)\n- Support `pathlib.Path()` objects by @singingwolfboy in [$450](https://github.com/rollbar/pyrollbar/pull/450)\n- Added support for Python 3.12 by @danielmorell in [#460](https://github.com/rollbar/pyrollbar/pull/460)\n- Changed the `ShortenerTransform` to use breadth first traversal by @danielmorell in [#461](https://github.com/rollbar/pyrollbar/pull/461)\n\n**1.1.0-alpha**\n\n- Fixed shortener multi level shortening by @danielmorell and @pawelsz-rb. See [#449](https://github.com/rollbar/pyrollbar/pull/449)\n\n**1.0.0**\n\n- Fixed handling `sensitive_post_parameters` decorator in Django by @pawelsz-rb. See [#413](https://github.com/rollbar/pyrollbar/pull/413)\n- Fixed Werkzeug DeprecationWarning of `BaseRequest`  by @compyman. See [#410](https://github.com/rollbar/pyrollbar/pull/410)\n- Fixed missing locals shortening on items with \"trace_chain\" instead of \"trace\" by @terencehonles. See [#365](https://github.com/rollbar/pyrollbar/pull/365)\n- Fixed FastAPI version comparison by @ayharano. See [#433](https://github.com/rollbar/pyrollbar/pull/433)\n- Fixed #436 `WSGIRequest` has no attribute `sensitive_post_parameters`. by @danielmorell. See [#437](https://github.com/rollbar/pyrollbar/pull/437)\n- Added new `thread_pool` handler by @danielmorell. See [#416](https://github.com/rollbar/pyrollbar/pull/416)\n- Added Rollbar branding to the readme by @paulserraino. See [#418](https://github.com/rollbar/pyrollbar/pull/418)\n- Added batched transform to increase sanitization and serialization performance by @ijsnow. See [#421](https://github.com/rollbar/pyrollbar/pull/421)\n- Replaced unittest2 with unittest by @brianr. See [#420](https://github.com/rollbar/pyrollbar/pull/420)\n- Removed unittest2 by @mcepl. See [#419](https://github.com/rollbar/pyrollbar/pull/419)\n- Remove deprecated flask `before_first_request` by @albertyw. See [#428](https://github.com/rollbar/pyrollbar/pull/428)\n- Removed support for Python 2 by @danielmorell. See [#435](https://github.com/rollbar/pyrollbar/pull/435)\n- Updated the base Ubuntu for CI to 20.04 test runner by @danielmorell and @waltjones. See [#427](https://github.com/rollbar/pyrollbar/pull/427)\n- Replaced `httpx.post` `data` kwarg with `content` by @ayharano. See [#425](https://github.com/rollbar/pyrollbar/pull/425)\n\n**0.16.3**\n\n- Pinned Python 2 CI tests to legacy version of dependencies. See [#408](https://github.com/rollbar/pyrollbar/pull/408)\n- Add PyPI badge for supported Python versions. See [#408](https://github.com/rollbar/pyrollbar/pull/401)\n- Add Django 4.0+ compatibility. See [#408](https://github.com/rollbar/pyrollbar/pull/400)\n- Update PR template. See [#408](https://github.com/rollbar/pyrollbar/pull/395)\n- SC-95272: Remove support for Python 3.3. See [#408](https://github.com/rollbar/pyrollbar/pull/394)\n\n**0.16.2**\n\n- Fix building person data in Django. See [#385](https://github.com/rollbar/pyrollbar/pull/385)\n- Fix circular error logging for non-HTTP events in Starlette. See [#390](https://github.com/rollbar/pyrollbar/pull/390)\n- Fix Python 3.4 builds. See [#389](https://github.com/rollbar/pyrollbar/pull/389)\n\n**0.16.1**\n\n- Fix PyPI artifacts\n\n**0.16.0**\n\n- Add support for FastAPI framework. See [#373](https://github.com/rollbar/pyrollbar/pull/373)\n- Add support for Starlette framework. See [#373](https://github.com/rollbar/pyrollbar/pull/373)\n- Add support for ASGI-based frameworks. See [#373](https://github.com/rollbar/pyrollbar/pull/373)\n- Add support for HTTPX async handler. See [#373](https://github.com/rollbar/pyrollbar/pull/373)\n- Add support for async report_exc_info and report_message. See [#373](https://github.com/rollbar/pyrollbar/pull/373)\n- Collect user IP from X-Forwarded-For, fall back to X-Real-Ip. See [#370](https://github.com/rollbar/pyrollbar/pull/370)\n- Improve examples. See [#368](https://github.com/rollbar/pyrollbar/pull/368)\n- Fix Python 3.3 builds. See [#374](https://github.com/rollbar/pyrollbar/pull/374)\n- Fix Flask 0.x builds. See [#376](https://github.com/rollbar/pyrollbar/pull/376)\n\n**0.15.2**\n\n- Add support for whitelist/blacklist for safelist/blocklist. See [#354](https://github.com/rollbar/pyrollbar/pull/343)\n- Add Twisted to the available frameworks. See [#360](https://github.com/rollbar/pyrollbar/pull/360)\n\n**0.15.1**\n\n- Add support to Python 3.8. See [#351](https://github.com/rollbar/pyrollbar/pull/351)\n- Fix deque test. See [#349](https://github.com/rollbar/pyrollbar/pull/349)\n- Add alternatives to exception handler. See [#335](https://github.com/rollbar/pyrollbar/pull/335)\n- Change dict_merge to allow strict mode. See [#339](https://github.com/rollbar/pyrollbar/pull/339)\n- Improve scrubbing test case. See [#343](https://github.com/rollbar/pyrollbar/pull/343)\n\n**0.15.0**\n\n- Prevent recursive re-raising of exceptions. See [#317](https://github.com/rollbar/pyrollbar/pull/317)\n- Correctly apply logger formatting to Rollbar messages. See [#312](https://github.com/rollbar/pyrollbar/pull/312)\n- Fix deprecation warnings. See [#325](https://github.com/rollbar/pyrollbar/pull/319) and [#331](https://github.com/rollbar/pyrollbar/pull/331)\n- Allow the request pool to be configured. See [#305](https://github.com/rollbar/pyrollbar/pull/305)\n- Use callable() instead of try/except TypeError. See [#319](https://github.com/rollbar/pyrollbar/pull/319)\n- Update Travis CI matrix. See [#317](https://github.com/rollbar/pyrollbar/pull/317)\n- Fix Travis build errors. See [#328](https://github.com/rollbar/pyrollbar/pull/328)\n- Update trove classifiers. See [#331](https://github.com/rollbar/pyrollbar/pull/331)\n\n**0.14.7**\n\n- Allow the raw request body to be included if desired. See [#304](https://github.com/rollbar/pyrollbar/pull/304)\n- Send Rollbar access token in HTTP header. See [#303](https://github.com/rollbar/pyrollbar/pull/303)\n- Add support for Django 1.7 & 1.8 in \\_build_django_request_data. See [#301](https://github.com/rollbar/pyrollbar/pull/301)\n- Add support for Quart framework. See [#300](https://github.com/rollbar/pyrollbar/pull/300)\n\n**0.14.6**\n\n- Add the authorization header to the default scrub fields list. See [#299](https://github.com/rollbar/pyrollbar/pull/299)\n- Encode the payload properly for newer versions of Twisted. See [#298](https://github.com/rollbar/pyrollbar/pull/298)\n- Don't fail to send payloads because some inner object is not JSON serializable. See [#297](https://github.com/rollbar/pyrollbar/pull/297)\n- Allow floats as circular references. See [#291](https://github.com/rollbar/pyrollbar/pull/291)\n\n**0.14.5**\n\n- Fix bug in which error params were not being passed correctly to Pyramid middleware. See [#287](https://github.com/rollbar/pyrollbar/pull/287)\n\n**0.14.4**\n\n- Fix bug in Pyramid middleware where exc_info was not being passed to handle_error. See [#283](https://github.com/rollbar/pyrollbar/pull/283)\n- Fix bug where errors in the serialization of local variables caused errors to be dropped. They will now make it to Rollbar. See [#284](https://github.com/rollbar/pyrollbar/pull/284)\n\n**0.14.3**\n\n- Add support for HTTP(S) proxies. See [#276](https://github.com/rollbar/pyrollbar/pull/276)\n\n**0.14.2**\n\n- Fix bug with file-based logging config in Python 3. See [#277](https://github.com/rollbar/pyrollbar/issues/277)\n- Fix bug in Django middleware when request has no META attribute. See [#273](https://github.com/rollbar/pyrollbar/pull/273)\n\n**0.14.1**\n\n- Add Django middlewares that differentiate between 404 and other exceptions. See [#270](https://github.com/rollbar/pyrollbar/pull/270)\n- Make Werkzeug request handling more general. See [#271](https://github.com/rollbar/pyrollbar/pull/271)\n- Fix incorrect handling rollbar.init() arguments when using LOGGER in Django. See [#235](https://github.com/rollbar/pyrollbar/pull/235)\n- Use thread local storage for not thead-safe requests.Sessions. See [#269](https://github.com/rollbar/pyrollbar/pull/269)\n- Swallow known error that happens in add_person_data. See [#268](https://github.com/rollbar/pyrollbar/pull/268)\n- Only write a log about a rate limit once. See [#267](https://github.com/rollbar/pyrollbar/pull/267)\n- Adapt Django view monkey patch to support Django 2. See [#257](https://github.com/rollbar/pyrollbar/pull/257)\n- Add support request objects from Django REST framework. See [#18](https://github.com/rollbar/pyrollbar/pull/18)\n- Add support for Falcon framework requests. See [#51](https://github.com/rollbar/pyrollbar/pull/51)\n- Add support for Django Channels' AsgiRequest. See [#272](https://github.com/rollbar/pyrollbar/pull/272)\n\n**0.14.0**\n\n- Create the configuration options, `capture_username` and `capture_email`. Prior to this release,\n  if we gather person data automatically, we would try to capture the id, email, and username.\n  Starting with this release by default we will only capture the id. If you set `capture_username`\n  to `True` then we will also attempt to capture the username. Similarly for `capture_email` with\n  the email. (See [#262](https://github.com/rollbar/pyrollbar/pull/262))\n- Create the configuration option `capture_ip`. This can take one of three values: `True`,\n  `'anonymize'`, or `False`. This controls how we handle IP addresses that are captured from\n  requests. If `True`, then we will send the full IP address. This is the current behaviour and the\n  default. If set to the string `'anonymize'` which is also available as the constant `ANONYMIZE` on\n  the `rollbar` module, we will mask out the least significant bits of the IP address. If set to\n  `False`, then we will not capture the IP address. (See [#262](https://github.com/rollbar/pyrollbar/pull/262))\n- Fix `request.files_keys` for Flask [#263](https://github.com/rollbar/pyrollbar/pull/263)\n- If you call `init` multiple times we will update the settings at each call. Prior to\n  this release we emitted a warning and did not update settings. [#259](https://github.com/rollbar/pyrollbar/pull/259)\n- Better Tornado support [#256](https://github.com/rollbar/pyrollbar/pull/256)\n\n**0.13.18**\n\n- See Release Notes\n\n**0.13.17**\n\n- Fix deprecation warning related to Logging.warn\n- Fix bug where non-copyable objects could cause an exception if they end up trying to get passed to\n  one of the logging methods.\n- Fix bug where both `trace` and `trace_chain` could appear in the final payload, which is not\n  allowed by the API.\n\n**0.13.16**\n\n- Fix PyPI documentation\n\n**0.13.15**\n\n- Fix shortener issue for Python 3\n\n**0.13.14**\n\n- Fix bug that caused some payload objects to be turned into the wrong type when\nshortening is applied. This would lead to API rejections. See [#200](https://github.com/rollbar/pyrollbar/pull/200)\n- Add `suppress_reinit_warning` option if you want to allow calling init twice. See [#198](https://github.com/rollbar/pyrollbar/pull/198)\n- Pass through keyword arguments from the logging handler to the underling Rollbar init call. See\n  [#203](https://github.com/rollbar/pyrollbar/pull/203)\n\n**0.13.13**\n\n- Add support for AWS Lambda. See [#191](https://github.com/rollbar/pyrollbar/pull/191)\n\n**0.13.12**\n\n- Remove the Django request body from the payload as it can contain sensitive data. See [#174](https://github.com/rollbar/pyrollbar/pull/174)\n- Allow users to shorten arbitrary parts of the payload. See [#173](https://github.com/rollbar/pyrollbar/pull/173)\n- Fix a Django deprecation warning. See [#165](https://github.com/rollbar/pyrollbar/pull/165)\n\n**0.13.11**\n\n- Handle environments where `sys.argv` does not exist. See [#131](https://github.com/rollbar/pyrollbar/pull/131)\n\n**0.13.10**\n\n- Gather request method from WebOb requests. See [#152](https://github.com/rollbar/pyrollbar/pull/152)\n\n**0.13.9**\n\n- Change `_check_config()` to deal with agent handler. See [#147](https://github.com/rollbar/pyrollbar/pull/147)\n- Fix settings values not being booleans in Pyramid. See [#150](https://github.com/rollbar/pyrollbar/pull/150)\n\n**0.13.8**\n\n- Fix regression from 0.13.7. See [#141](https://github.com/rollbar/pyrollbar/pull/141)\n\n**0.13.7**\n\n- Update Django middleware to support Django 1.10+. See [#138](https://github.com/rollbar/pyrollbar/pull/138)\n\n**0.13.6**\n\n- Fixed a referenced before assignment in the failsafe. See [#136](https://github.com/rollbar/pyrollbar/pull/136)\n\n**0.13.5**\n\n- Fixed record message formatting issues breaking the log handler's history. See [#135](https://github.com/rollbar/pyrollbar/pull/135)\n\n**0.13.4**\n\n- Fixed failsafe handling for payloads that are too large. See [#133](https://github.com/rollbar/pyrollbar/pull/133)\n\n**0.13.3**\n\n- Improved handling of Enums. See [#121](https://github.com/rollbar/pyrollbar/pull/121)\n\n**0.13.2**\n\n- Improved handling of Nan and (Negative)Infinity. See [#117](https://github.com/rollbar/pyrollbar/pull/117)\n- RollbarHandler now ignores log records from Rollbar. See [#118](https://github.com/rollbar/pyrollbar/pull/118)\n\n**0.13.1**\n\n- Failsafe handling for payloads that are too large. See [#116](https://github.com/rollbar/pyrollbar/pull/116)\n  - Failsafe Behavior\n    - Log an error containing the original payload and the UUID from it\n    - Send a new payload to Rollbar with the custom attribute containing the UUID and host from the original payload\n\n**0.13.0**\n\n- Frame payload refactor and varargs scrubbing. See [#113](https://github.com/rollbar/pyrollbar/pull/113)\n  - Frame Payload Changes\n    - remove args and kwargs\n    - add argspec as the list of argument names to the function call\n    - add varargspec as the name of the list containing the arbitrary unnamed positional arguments to the function call if any exist\n    - add keywordspec as the name of the object containing the arbitrary keyword arguments to the function call if any exist\n  - Other Changes:\n    - Arguments with default values are no longer removed from args and placed into kwargs\n    - varargs are now scrubbable and scrubbed by default\n- Switched to using a Session object to perform HTTPS requests to optimize for keepalive connections. See [#114](https://github.com/rollbar/pyrollbar/pull/114)\n\n**0.12.1**\n\n- Keep blank values from request query strings when scrubbing URLs. See [#110](https://github.com/rollbar/pyrollbar/pull/110)\n\n**0.12.0**\n\n- Fix and update Twisted support. See [#109](https://github.com/rollbar/pyrollbar/pull/109)\n  - **Breaking Changes**: [treq](https://github.com/twisted/treq) is now required for using Twisted with pyrollbar.\n\n**0.11.6**\n\n- Improve object handling for SQLAlchemy. See [#108](https://github.com/rollbar/pyrollbar/pull/108)\n\n**0.11.5**\n\n- Fixed a bug when custom `__repr__()` calls resulted in an exception being thrown. See [#102](https://github.com/rollbar/pyrollbar/pull/102)\n\n**0.11.4**\n\n- Revert changes from 0.11.3 since they ended-up having the unintended side effect by that exceptions messages weren't processing as expected.\n- Update settings in init first so that custom scrub_fields entries are handled correctly\n\n**0.11.3**\n\n- Obey safe repr for exceptions.  See [#91](https://github.com/rollbar/pyrollbar/pull/91)\n\n**0.11.2**\n- Fixed a bug when calling logging.exception() when not in an exception handler.  Now it correctly determines it doesn't have any exception info and uses report_message() instead of report_exc_info().\n\n**0.11.1**\n- Added a new configuration option to expose the serializer's `safelisted_types` param\n  - Allows users to safelist types to be serialized using `repr(obj)` instead of `str(type(obj))`\n- Fixed a bug that was not taking the `safe_repr` option into account. See [#87](https://github.com/rollbar/pyrollbar/pull/87)\n\n**0.11.0**\n- Overhauled the scrubbing and serialization mechanisms to provide deep object scrubbing and better handling of UTF-8 data from local variables. See [#75](https://github.com/rollbar/pyrollbar/pull/75)\n  - This fixes a bunch of problems with reporting local variables, including `UnicodeEncodeError`s and attempting to read variables after the thread they were in has died.\n- Local variables and payload data is now sent over in their original structure.\n  - If a variable was a `dict`, it will be transmitted as a `dict` instead of turned into a string representation of the variable.\n- The entire payload is now scrubbed and URL password fields are scrubbed as well.\n- Added a Django example.\n- Wrote many, many more tests :)\n- Integrated the `six` library to provide cleaner support for Python3.\n- Added some additional scrub fields.\n\n**0.10.1**\n- Added a warning message if `init()` is called more than once.\n\n**0.10.0**\n- Added support for Twisted framework. See [#69](https://github.com/rollbar/pyrollbar/pull/69)\n- Fix a bug that was causing max recursion errors while collecting local variables. See [#77](https://github.com/rollbar/pyrollbar/pull/77)\n  - Added a configuration option, `safe_repr: True` which will cause payload serialization to use the type name for non-built-in objects.\n    This option defaults to `True` which may cause data reported to Rollbar to contain less information for custom types.\n    Prior to this change, serialization of custom objects called `__repr__()` which may have had undesired side effects.\n- Fixed a bug that did not correctly handle anonymous tuple arguments while gathering local variables.\n\n**0.9.14**\n- Fix logging loop when using Flask in a non-request context, and also using the Rollbar logging handler. See [#68](https://github.com/rollbar/pyrollbar/pull/68)\n\n**0.9.13**\n- If present, get request from log record. Otherwise try to guess current request as usual.\n\n**0.9.12**\n- Fix a bug that was causing a crash while reporting an error that happened in a Werkzeug request that had no `request.json`. See [#64](https://github.com/rollbar/pyrollbar/pull/64)\n\n**0.9.11**\n- Implement workarounds for NaN and Infinity \"numbers\" in payloads. See [#62](https://github.com/rollbar/pyrollbar/pull/62)\n\n**0.9.10**\n- Fix request data collection in Flask 0.9. See [#61](https://github.com/rollbar/pyrollbar/pull/61)\n\n**0.9.9**\n- Add exception handler for RQ (requires some instrumentation). See [#57](https://github.com/rollbar/pyrollbar/pull/57)\n- Scrub fields inside `extra_data`\n- Gather the process PID and report it along with the other 'server' data\n\n**0.9.8**\n- Support bare WSGI requests ([#55](https://github.com/rollbar/pyrollbar/pull/55))\n\n**0.9.7**\n- Add support for Google App Engine ([#53](https://github.com/rollbar/pyrollbar/pull/53))\n\n**0.9.6**\n- Fix memory leak when using the RollbarHandler logging handler (see [#43](https://github.com/rollbar/pyrollbar/pull/43))\n- Fix bug where named function arguments were not scrubbed correctly\n\n**0.9.5**\n- Fix bug with local variable gathering that was breaking when getting the arguments for a class constructor.\n\n**0.9.4**\n- Request headers are now scrubbed, [pr#41](https://github.com/rollbar/pyrollbar/pull/41).\n\n**0.9.3**\n- `exception_level_filters` can now take a string that defines the class to filter, [#38](https://github.com/rollbar/pyrollbar/pull/38).\n\n**0.9.2**\n- Added an option to disable SSL certificate verification, [#36](https://github.com/rollbar/pyrollbar/pull/36).\n- Added `__version__` specifier to `__init__.py`.\n\n**0.9.1**\n\nNew features:\n\n- For Tornado requests, gather the request start time. See [#33](https://github.com/rollbar/pyrollbar/pull/33)\n- Add handler which uses Tornado's `AsyncHTTPClient`. To use this, set your 'handler' to 'tornado'. See [#34](https://github.com/rollbar/pyrollbar/pull/34)\n\n\n**0.9.0**\n\n- Improvements to RollbarHandler logging handler. It now:\n  - extracts more information out of each record (i.e. metadata like pathname and creation time)\n  - uses the format string, with arguments not yet replaced, as the main message body. This will result in much better grouping in Rollbar.\n\nNote about upgrading from 0.8.x: unless you are using RollbarHandler, there are no breaking changes. If you are using RolbarHandler, then this will change the way your data appears in Rollbar (to the better, in our opinion).\n\n**0.8.3**\n- Provide a way to blocklist types from being repr()'d while gathering local variables.\n\n**0.8.2**\n- Fix uncaught ImproperlyConfigured exception when importing Rollbar in a Django REST Framework environment without a settings module loaded ([#28](https://github.com/rollbar/pyrollbar/pull/28))\n\n**0.8.1**\n- Only attempt local variable extraction if traceback frames are of the correct type, print a warning otherwise\n- Fix JSON request param extraction for Werkzeug requests (Pyramid, Flask, etc)\n\n**0.8.0**\n- Local variables collection now enabled by default.\n- Fixed scrubbing for utf8 param names.\n\n**0.7.6**\n- Added local variables for all in-project frames and the last frame.\n\n**0.7.5**\n- Initial support for sending args and kwargs for traceback frames.\n- Optimization to send the access token in a header.\n\n**0.7.4**\n- Level kwarg added to `rollbar.report_exc_info()` ([#22](https://github.com/rollbar/pyrollbar/pull/22))\n\n**0.7.3**\n- Added in an optional `endpoint` parameter to `search_items()`.\n\n**0.7.2**\n- Fix for scrubbing werkzeug json bodies ([#20](https://github.com/rollbar/pyrollbar/pull/20))\n\n**0.7.1**\n- Support scrubbing for werkzeug json bodies ([#19](https://github.com/rollbar/pyrollbar/pull/19))\n\n**0.7.0**\n- Python 3 support\n- Now support extracting data from Django REST framework requests\n- New `enabled` configuration setting\n\n**0.6.2**\n- Fixed json request data formatting for reports in Bottle requests\n- Now send json request data for Django and Pyramid apps\n- Set framework and request context properly for all reports in Flask and Bottle apps\n\n**0.6.1**\n- Added Django, Pyramid, Flask and Bottle support for default contexts.\n\n**0.6.0**\n- `report_message()` now returns the UUID of the reported occurrence.\n\n**0.5.14**\n- Fix bug with non-JSON post data in Flask\n- Add slightly better integration with Flask. See [rollbar-flask-example](https://github.com/rollbar/rollbar-flask-example) for example usage.\n\n**0.5.13**\n- Collect JSON post data in Flask when mimetype is `application/json`\n\n**0.5.12**\n- Add sys.argv to server data\n\n**0.5.11**\n- Don't report bottle.BaseResponse exceptions in the bottle plugin\n\n**0.5.10**\n- Added `code_version` configuration setting\n- Added support for bottle request objects\n\n**0.5.9**\n- Added a command line interface for reporting messages to Rollbar\n\n**0.5.8**\n- Added `allow_logging_basic_config` config flag for compatability with Flask. If using Flask, set to False.\n\n**0.5.7**\n- Added `exception_level_filters` configuration setting to customize the level that specific exceptions are reported as.\n\n**0.5.6**\n- First argument to `rollbar.report_exc_info()` is now optional. You can now call it with no arguments from within an `except` block, and it will behave is if you had called like `rollbar.report_exc_info(sys.exc_info())`\n\n**0.5.5**\n- Support for ignoring exceptions by setting `exc._rollbar_ignore = True`. Such exceptions reported through rollbar.report_exc_info() -- which is used under the hood in the Django and Pyramid middlewares -- will be ignored instead of reported.\n\n**0.5.4**\n- Django: catch exceptions when patching the debugview, for better support for django 1.3.\n\n**0.5.3**\n- Fixed bug when reporting messages without a request object\n\n**0.5.2**\n- Fixed bug where django debug page can get patched twice\n\n**0.5.1**\n- Catching possible malformed API responses\n\n**0.5.0**\n- Rename to rollbar\n\n**0.4.1**\n- report_exc_info() now takes two additional named args: `extra_data` and `payload_data`, like report_message().\n- on 429 response (over rate limit), log a warning but don't parse and print an exception.\n\n**0.3.2**\n- Added new default scrub fields\n\n**0.3.1**\n- Fixed pypi package\n\n**0.3.0**\n- Merge django-ratchet and pyramid_ratchet into pyratchet\n- Add ability to write to a ratchet-agent log file\n\n**0.2.0**\n- Add \"person\" support\n\n**0.1.14**\n- Added payload_data arg to report_message()\n\n**0.1.13**\n- Added extra_data arg to report_message()\n\n**0.1.12**\n- Use custom JSON encoder to skip objects that can't be encoded.\n- Bump default timeout from 1 to 3 seconds.\n\n**0.1.11**\n- Sensitive params now scrubbed out of POST. Param name list is customizable via the `scrub_fields` config option.\n\n**0.1.10**\n- Add support for Tornado request objects (`tornado.httpserver.HTTPRequest`)\n\n**0.1.9**\n- Fix support for Pyramid request objects\n\n**0.1.8**\n- Add support for Django request objects\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2014 Rollbar, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img alt=\"rollbar-logo\" src=\"https://user-images.githubusercontent.com/3300063/207964480-54eda665-d6fe-4527-ba51-b0ab3f41f10b.png\" />\n</p>\n\n<h1 align=\"center\">Pyrollbar</h1>\n\n<p align=\"center\">\n  <strong>Proactively discover, predict, and resolve errors in real-time with <a href=\"https://rollbar.com\">Rollbar’s</a> error monitoring platform. <a href=\"https://rollbar.com/signup/\">Start tracking errors today</a>!</strong>\n</p>\n\n\n[![Build Status](https://github.com/rollbar/pyrollbar/workflows/Pyrollbar%20CI/badge.svg?tag=latest)](https://github.com/rollbar/pyrollbar/actions)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/rollbar)\n\nPython notifier for reporting exceptions, errors, and log messages to [Rollbar](https://rollbar.com).\n\n## Key benefits of using Pyrollbar are:\n- **Frameworks:** Pyrollbar supports popular Python frameworks such as <a href=\"https://docs.rollbar.com/docs/django\">Django</a>, <a href=\"https://docs.rollbar.com/docs/flask\">Flask</a>, <a href=\"https://docs.rollbar.com/docs/fastapi\">FastAPI</a>, <a href=\"https://docs.rollbar.com/docs/aws-lambda-1\">AWS Lambda</a> and more!\n- **Automatic error grouping:** Rollbar aggregates Occurrences caused by the same error into Items that represent application issues. <a href=\"https://docs.rollbar.com/docs/grouping-occurrences\">Learn more about reducing log noise</a>.\n- **Advanced search:** Filter items by many different properties. <a href=\"https://docs.rollbar.com/docs/search-items\">Learn more about search</a>.\n- **Customizable notifications:** Rollbar supports several messaging and incident management tools where your team can get notified about errors and important events by real-time alerts. <a href=\"https://docs.rollbar.com/docs/notifications\">Learn more about Rollbar notifications</a>.\n\n## Python Versions Supported\n\n| PyRollbar Version | Python Version Compatibility                  | Support Level       |\n|-------------------|-----------------------------------------------|---------------------|\n| 1.4.0             | 3.9, 3.10, 3.11, 3.12, 3.13, 3.14             | Full                |\n| 0.16.3            | 2.7, 3.4, 3.5, 3.6, 3.7. 3.8, 3.9, 3.10, 3.11 | Security Fixes Only |\n\n#### Support Level Definitions\n\n**Full** - We will support new features of the library and test against all supported versions.\n\n**Security Fixes Only** - We will only provide critical security fixes for the library.\n\n## Frameworks Supported\n\nGenerally, PyRollbar can be used with any Python framework. However, we have official support for the following frameworks:\n\n| Framework | Support Duration           | Tested Versions |\n|-----------|----------------------------|-----------------|\n| Celery    | Release +1 year            | None            |\n| Django    | Release or LTS end +1 year | 4.2, 5.2        |\n| FastAPI   | Release +1 year            | 0.115, 0.118    |\n| Flask     | Release +1 year            | 2.3, 3.1        |\n| Pyramid   | Release +1 year            | 1.10, 2.0       |\n\nOfficial support means that we ship and maintain integrations for these frameworks. It also means that we test against these frameworks as part of our CI pipeline.\n\nGenerally, we will support the last year of releases for a framework. If a framework has a defined support period (including LTS releases), we will support the release for the duration of that period plus one year.\n\n### Community Supported\n\nThere are also a number of community-supported integrations available. For more information, see the [Python SDK docs](https://docs.rollbar.com/docs/python-community-supported-sdks).\n\n## Setup Instructions\n\n1. [Sign up for a Rollbar account](https://rollbar.com/signup)\n2. Follow the [Quick Start](https://docs.rollbar.com/docs/python#section-quick-start) instructions in our [Python SDK docs](https://docs.rollbar.com/docs/python) to install pyrollbar and configure it for your platform.\n\n## Usage and Reference\n\nFor complete usage instructions and configuration reference, see our [Python SDK docs](https://docs.rollbar.com/docs/python).\n\n## Release History & Changelog\n\nSee our [Releases](https://github.com/rollbar/pyrollbar/releases) page for a list of all releases, including changes.\n\n## Help / Support\n\nIf you run into any issues, please email us at [support@rollbar.com](mailto:support@rollbar.com)\n\nFor bug reports, please [open an issue on GitHub](https://github.com/rollbar/pyrollbar/issues/new).\n\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (```git checkout -b my-new-feature```).\n3. Commit your changes (```git commit -am 'Added some feature'```)\n4. Push to the branch (```git push origin my-new-feature```)\n5. Create new Pull Request\n\nTests are in `rollbar/test`. To run the tests: `python setup.py test`\n"
  },
  {
    "path": "THANKS.md",
    "content": "# Contributors\n\nHuge thanks to the following contributors (by github username). For the most up-to-date list, see https://github.com/rollbar/pyrollbar/graphs/contributors\n\n- [alex-laties](https://github.com/alex-laties)\n- [dmitry-mukhin](https://github.com/dmitry-mukhin)\n- [homm](https://github.com/homm)\n- [isra17](https://github.com/isra17)\n- [jamesonjlee](https://github.com/jamesonjlee)\n- [jbowes](https://github.com/jbowes)\n- [jbrumwell](https://github.com/jbrumwell)\n- [jedipi](https://github.com/jedipi)\n- [juggernaut](https://github.com/juggernaut)\n- [rhcarvalho](https://github.com/rhcarvalho)\n- [tschieggm](https://github.com/tschieggm)\n- [zvirusz](https://github.com/zvirusz)\n- [benkuhn](https://github.com/benkuhn)\n- [pmourlanne](https://github.com/pmourlanne)\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools>=61.0\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"rollbar\"\ndynamic = [\"version\"]\ndescription = \"Easy and powerful exception tracking with Rollbar. Send messages and exceptions with arbitrary context, get back aggregates, and debug production issues quickly.\"\nreadme = \"README.md\"\nlicense = {file = \"LICENSE\"}\nmaintainers = [{name = \"Rollbar, Inc.\", email = \"support@rollbar.com\"}]\nclassifiers = [\n    \"Programming Language :: Python\",\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3.9\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n    \"Programming Language :: Python :: 3.12\",\n    \"Programming Language :: Python :: 3.13\",\n    \"Programming Language :: Python :: 3.14\",\n    \"Programming Language :: Python :: 3 :: Only\",\n    \"License :: OSI Approved :: MIT License\",\n    \"Operating System :: OS Independent\",\n    \"Development Status :: 5 - Production/Stable\",\n    \"Environment :: Web Environment\",\n    \"Framework :: AsyncIO\",\n    \"Framework :: Bottle\",\n    \"Framework :: Django\",\n    \"Framework :: Flask\",\n    \"Framework :: Pylons\",\n    \"Framework :: Pyramid\",\n    \"Framework :: Twisted\",\n    \"Intended Audience :: Developers\",\n    \"Topic :: Internet :: WWW/HTTP\",\n    \"Topic :: Software Development\",\n    \"Topic :: Software Development :: Bug Tracking\",\n    \"Topic :: Software Development :: Testing\",\n    \"Topic :: Software Development :: Quality Assurance\",\n    \"Topic :: System :: Logging\",\n    \"Topic :: System :: Monitoring\",\n]\nrequires-python = \">=3.9\"\ndependencies = [\n    \"requests>=0.12.1\",\n]\n\n[dependency-groups]\ntest = [\n    \"blinker\",\n    \"httpx\",\n    \"pytest\",\n    \"webob\",\n]\n\n[project.urls]\nHomepage = \"https://rollbar.com/\"\nDocumentation = \"https://docs.rollbar.com/docs/python\"\nChanges = \"https://github.com/rollbar/pyrollbar/blob/master/CHANGELOG.md\"\nSource = \"https://github.com/rollbar/pyrollbar/\"\n\n[project.scripts]\nrollbar = \"rollbar.cli:main\"\n\n[project.entry-points.\"paste.filter_app_factory\"]\npyramid = \"rollbar.contrib.pyramid:create_rollbar_middleware\"\n\n[tool.pytest]\ntestpaths = [\n    \"rollbar/test\",\n]\n\n[tool.setuptools.dynamic]\nversion = {attr = \"rollbar.__version__\"}\n\n[tool.tox]\nrequires = [\"tox>=4.22\"]\n\n[tool.tox.env_run_base]\ndependency_groups = [\"test\"]\ndescription = \"run unit tests\"\ncommands = [[\"pytest\", \"{posargs}\"]]\n"
  },
  {
    "path": "rollbar/__init__.py",
    "content": "from __future__ import absolute_import, annotations\nfrom __future__ import unicode_literals\n\nimport copy\nimport functools\nimport inspect\nimport json\nimport logging\nimport os\nimport socket\nimport sys\nimport threading\nimport time\nimport traceback\nimport types\nimport uuid\nimport wsgiref.util\nimport warnings\nimport queue\nfrom urllib.parse import parse_qs, urljoin\n\nimport requests\n\nfrom rollbar.lib import events, filters, dict_merge, transport, defaultJSONEncode\nfrom rollbar.lib.payload import Attribute\nfrom rollbar.lib.session import get_current_session, set_current_session, parse_session_request_baggage_headers\n\n__version__ = '1.4.0-beta'\n__log_name__ = 'rollbar'\nlog = logging.getLogger(__log_name__)\n\n\n# import request objects from various frameworks, if available\ntry:\n    from webob import BaseRequest as WebobBaseRequest\nexcept ImportError:\n    WebobBaseRequest = None\n\ntry:\n    from django.core.exceptions import ImproperlyConfigured\nexcept ImportError:\n    DjangoHttpRequest = None\n    RestFrameworkRequest = None\n\nelse:\n    try:\n        from django.http import HttpRequest as DjangoHttpRequest\n    except (ImportError, ImproperlyConfigured):\n        DjangoHttpRequest = None\n\n    try:\n        from rest_framework.request import Request as RestFrameworkRequest\n    except (ImportError, ImproperlyConfigured):\n        RestFrameworkRequest = None\n\n    del ImproperlyConfigured\n\ntry:\n    from werkzeug.wrappers import Request as WerkzeugRequest\nexcept (ImportError, SyntaxError):\n    WerkzeugRequest = None\n\ntry:\n    from werkzeug.local import LocalProxy as WerkzeugLocalProxy\nexcept (ImportError, SyntaxError):\n    WerkzeugLocalProxy = None\n\ntry:\n    from tornado.httpserver import HTTPRequest as TornadoRequest\nexcept ImportError:\n    TornadoRequest = None\n\ntry:\n    from bottle import BaseRequest as BottleRequest\nexcept ImportError:\n    BottleRequest = None\n\ntry:\n    from sanic.request import Request as SanicRequest\nexcept ImportError:\n    SanicRequest = None\n\ntry:\n    from google.appengine.api.urlfetch import fetch as AppEngineFetch\nexcept (ImportError, KeyError):\n    AppEngineFetch = None\n\ntry:\n    from starlette.requests import Request as StarletteRequest\nexcept ImportError:\n    StarletteRequest = None\n\ntry:\n    from fastapi.requests import Request as FastAPIRequest\nexcept ImportError:\n    FastAPIRequest = None\n\ntry:\n    import httpx\nexcept ImportError:\n    httpx = None\n\nAsyncHTTPClient = httpx\n\ndef passthrough_decorator(func):\n    def wrap(*args, **kwargs):\n        return func(*args, **kwargs)\n    return wrap\n\ntry:\n    from tornado.httpclient import AsyncHTTPClient as TornadoAsyncHTTPClient\nexcept ImportError:\n    TornadoAsyncHTTPClient = None\n\ntry:\n    import treq\n    from twisted.python import log as twisted_log\n    from twisted.web.iweb import IPolicyForHTTPS\n    from twisted.web.client import BrowserLikePolicyForHTTPS, Agent\n    from twisted.internet.ssl import CertificateOptions\n    from twisted.internet import task, defer, ssl, reactor\n    from zope.interface import implementer\n\n    @implementer(IPolicyForHTTPS)\n    class VerifyHTTPS(object):\n        def __init__(self):\n            # by default, handle requests like a browser would\n            self.default_policy = BrowserLikePolicyForHTTPS()\n\n        def creatorForNetloc(self, hostname, port):\n            # check if the hostname is in the the whitelist, otherwise return the default policy\n            if not SETTINGS['verify_https']:\n                return ssl.CertificateOptions(verify=False)\n            return self.default_policy.creatorForNetloc(hostname, port)\n\n    def log_handler(event):\n        \"\"\"\n        Default uncaught error handler\n        \"\"\"\n        try:\n            if not event.get('isError') or 'failure' not in event:\n                return\n\n            err = event['failure']\n\n            # Don't report Rollbar internal errors to ourselves\n            if issubclass(err.type, ApiException):\n                log.error('Rollbar internal error: %s', err.value)\n            else:\n                report_exc_info((err.type, err.value, err.getTracebackObject()))\n        except:\n            log.exception('Error while reporting to Rollbar')\n\n    # Add Rollbar as a log handler which will report uncaught errors\n    twisted_log.addObserver(log_handler)\n\n\nexcept ImportError:\n    treq = None\n\ntry:\n    from falcon import Request as FalconRequest\nexcept ImportError:\n    FalconRequest = None\n\n\ndef get_request():\n    \"\"\"\n    Get the current request object. Implementation varies on\n    library support. Modified below when we know which framework\n    is being used.\n    \"\"\"\n\n    # TODO(cory): add in a generic _get_locals_request() which\n    # will iterate up through the call stack and look for a variable\n    # that appears to be valid request object.\n    for fn in (_get_fastapi_request,\n               _get_starlette_request,\n               _get_bottle_request,\n               _get_flask_request,\n               _get_pyramid_request,\n               _get_pylons_request):\n        try:\n            req = fn()\n            if req is not None:\n                return req\n        except:\n            pass\n\n    return None\n\n\ndef _get_bottle_request():\n    if BottleRequest is None:\n        return None\n    from bottle import request\n    return request\n\n\ndef _get_flask_request():\n    if WerkzeugRequest is None:\n        return None\n    from flask import request\n    return request\n\n\ndef _get_pyramid_request():\n    if WebobBaseRequest is None:\n        return None\n    from pyramid.threadlocal import get_current_request\n    return get_current_request()\n\n\ndef _get_pylons_request():\n    if WebobBaseRequest is None:\n        return None\n    from pylons import request\n    return request\n\n\ndef _get_starlette_request():\n    # Do not modify the returned object\n\n    if StarletteRequest is None:\n        return None\n\n    from rollbar.contrib.starlette import get_current_request\n    return get_current_request()\n\n\ndef _get_fastapi_request():\n    # Do not modify the returned object\n\n    if FastAPIRequest is None:\n        return None\n\n    from rollbar.contrib.fastapi import get_current_request\n    return get_current_request()\n\n\nBASE_DATA_HOOK = None\n\nagent_log = None\n\nVERSION = __version__\nDEFAULT_ENDPOINT = 'https://api.rollbar.com/api/1/'\nDEFAULT_TIMEOUT = 3\nANONYMIZE = 'anonymize'\n\nDEFAULT_LOCALS_SIZES = {\n    'maxlevel': 5,\n    'maxdict': 10,\n    'maxlist': 10,\n    'maxtuple': 10,\n    'maxset': 10,\n    'maxfrozenset': 10,\n    'maxdeque': 10,\n    'maxarray': 10,\n    'maxstring': 100,\n    'maxlong': 40,\n    'maxother': 100,\n}\n\n# configuration settings\n# configure by calling init() or overriding directly\nSETTINGS = {\n    'access_token': None,\n    'enabled': True,\n    'environment': 'production',\n    'exception_level_filters': [],\n    'root': None,  # root path to your code\n    'host': None,  # custom hostname of the current host\n    'branch': None,  # git branch name\n    'code_version': None,\n    # 'blocking', 'thread' (default), 'async', 'agent', 'tornado', 'gae', 'twisted', 'httpx' or 'thread_pool'\n    # 'async' requires Python 3.4 or higher.\n    # 'httpx' requires Python 3.7 or higher.\n    # 'thread_pool' requires Python 3.2 or higher.\n    'handler': 'default',\n    'thread_pool_workers': None,\n    'endpoint': DEFAULT_ENDPOINT,\n    'timeout': DEFAULT_TIMEOUT,\n    'agent.log_file': 'log.rollbar',\n    'scrub_fields': [\n        'pw',\n        'passwd',\n        'password',\n        'secret',\n        'confirm_password',\n        'confirmPassword',\n        'password_confirmation',\n        'passwordConfirmation',\n        'access_token',\n        'accessToken',\n        'auth',\n        'authentication',\n        'authorization',\n    ],\n    'url_fields': ['url', 'link', 'href'],\n    'notifier': {\n        'name': 'pyrollbar',\n        'version': VERSION\n    },\n    'allow_logging_basic_config': True,  # set to False to avoid a call to logging.basicConfig()\n    'locals': {\n        'enabled': True,\n        'safe_repr': True,\n        'scrub_varargs': True,\n        'sizes': DEFAULT_LOCALS_SIZES,\n        'safelisted_types': [],\n        'whitelisted_types': []\n    },\n    'verify_https': True,\n    'shortener_keys': [],\n    'suppress_reinit_warning': False,\n    'capture_email': False,\n    'capture_username': False,\n    'capture_ip': True,\n    'log_all_rate_limited_items': True,\n    'log_payload_on_error': True,\n    'http_proxy': None,\n    'http_proxy_user': None,\n    'http_proxy_password': None,\n    'include_request_body': False,\n    'request_pool_connections': None,\n    'request_pool_maxsize': None,\n    'request_max_retries': None,\n    'batch_transforms': False,\n    'custom_transforms': [],\n}\n\n_CURRENT_LAMBDA_CONTEXT = None\n_LAST_RESPONSE_STATUS = None\n\n# Set in init()\n_transforms = []\n_serialize_transform = None\n_scrub_redact_transform = None\n\n_initialized = False\n\nfrom rollbar.lib.transforms.scrub_redact import REDACT_REF\n\nfrom rollbar.lib import transforms\nfrom rollbar.lib import type_info\nfrom rollbar.lib.transforms.scrub import ScrubTransform\nfrom rollbar.lib.transforms.scruburl import ScrubUrlTransform\nfrom rollbar.lib.transforms.scrub_redact import ScrubRedactTransform\nfrom rollbar.lib.transforms.serializable import SerializableTransform\nfrom rollbar.lib.transforms.shortener import ShortenerTransform\nfrom rollbar.lib.transforms.batched import BatchedTransform\n\n\n## public api\n\ndef init(access_token, environment='production', scrub_fields=None, url_fields=None, **kw):\n    \"\"\"\n    Saves configuration variables in this module's SETTINGS.\n\n    access_token: project access token. Get this from the Rollbar UI:\n                  - click \"Settings\" in the top nav\n                  - click \"Projects\" in the left nav\n                  - copy-paste the appropriate token.\n    environment: environment name. Can be any string; suggestions: 'production', 'development',\n                 'staging', 'yourname'\n    **kw: provided keyword arguments will override keys in SETTINGS.\n    \"\"\"\n    global SETTINGS, agent_log, _initialized, _transforms, _serialize_transform, _scrub_redact_transform, _threads\n\n    if scrub_fields is not None:\n       SETTINGS['scrub_fields'] = list(scrub_fields)\n    if url_fields is not None:\n       SETTINGS['url_fields'] = list(url_fields)\n\n    # Merge the extra config settings into SETTINGS\n    SETTINGS = dict_merge(SETTINGS, kw)\n    if _initialized:\n        # NOTE: Temp solution to not being able to re-init.\n        # New versions of pyrollbar will support re-initialization\n        # via the (not-yet-implemented) configure() method.\n        if not SETTINGS.get('suppress_reinit_warning'):\n            log.warning('Rollbar already initialized. Ignoring re-init.')\n        return\n\n    SETTINGS['access_token'] = access_token\n    SETTINGS['environment'] = environment\n    _configure_transport(**SETTINGS)\n\n    if SETTINGS.get('allow_logging_basic_config'):\n        logging.basicConfig()\n\n    if SETTINGS.get('handler') == 'agent':\n        agent_log = _create_agent_log()\n    elif SETTINGS.get('handler') == 'thread_pool':\n        from rollbar.lib.thread_pool import init_pool\n        init_pool(SETTINGS.get('thread_pool_workers', None))\n\n    if not SETTINGS['locals']['safelisted_types'] and SETTINGS['locals']['whitelisted_types']:\n        warnings.warn('whitelisted_types deprecated use safelisted_types instead', DeprecationWarning)\n        SETTINGS['locals']['safelisted_types'] = SETTINGS['locals']['whitelisted_types']\n\n    # We will perform these transforms in order:\n    # 1. Serialize the payload to be all python built-in objects\n    # 2. Scrub the payloads based on the key suffixes in SETTINGS['scrub_fields']\n    # 3. Scrub URLs in the payload for keys that end with 'url'\n    # 4. Optional - If local variable gathering is enabled, transform the\n    #       trace frame values using the ShortReprTransform.\n    _serialize_transform = SerializableTransform(safe_repr=SETTINGS['locals']['safe_repr'],\n                                                 safelist_types=SETTINGS['locals']['safelisted_types'])\n\n    _scrub_redact_transform = ScrubRedactTransform(suffixes=[(field,) for field in SETTINGS['scrub_fields']], redact_char='*')\n\n    # A list of key prefixes to apply our shortener transform to. The request\n    # being included in the body key is old behavior and is being retained for\n    # backwards compatibility.\n    shortener_keys = [\n        ('request', 'POST'),\n        ('request', 'json'),\n        ('body', 'request', 'POST'),\n        ('body', 'request', 'json'),\n    ]\n\n    if SETTINGS['locals']['enabled']:\n        for prefix in (('body', 'trace'), ('body', 'trace_chain', '*')):\n            shortener_keys.append(prefix + ('frames', '*', 'code'))\n            shortener_keys.append(prefix + ('frames', '*', 'args', '*'))\n            shortener_keys.append(prefix + ('frames', '*', 'kwargs', '*'))\n            shortener_keys.append(prefix + ('frames', '*', 'locals', '*'))\n\n    shortener_keys.extend(SETTINGS['shortener_keys'])\n\n    shortener = ShortenerTransform(safe_repr=SETTINGS['locals']['safe_repr'],\n                                   keys=shortener_keys,\n                                   **SETTINGS['locals']['sizes'])\n    _transforms = [\n        shortener,  # priority: 10\n        _scrub_redact_transform,  # priority: 20\n        _serialize_transform,  # priority: 30\n        ScrubUrlTransform(suffixes=[(field,) for field in SETTINGS['url_fields']],\n                          params_to_scrub=SETTINGS['scrub_fields'])  # priority: 50\n    ]\n\n    # Add custom transforms\n    if len(SETTINGS['custom_transforms']) > 0:\n        _transforms.extend(SETTINGS['custom_transforms'])\n\n    # Sort the transforms by priority\n    _transforms = sorted(_transforms, key=lambda x: x.priority)\n\n    _threads = queue.Queue()\n    events.reset()\n    filters.add_builtin_filters(SETTINGS)\n\n    _initialized = True\n\n\ndef _configure_transport(**kw):\n    configuration = _requests_configuration(**kw)\n    transport.configure_pool(**configuration)\n\n\ndef _requests_configuration(**kw):\n    keys = {\n        'request_pool_connections': 'pool_connections',\n        'request_pool_maxsize': 'pool_maxsize',\n        'request_max_retries': 'max_retries',\n    }\n    return {keys[k]: kw.get(k, None) for k in keys}\n\n\ndef lambda_function(f):\n    \"\"\"\n    Decorator for making error handling on AWS Lambda easier\n    \"\"\"\n    @functools.wraps(f)\n    def wrapper(event, context):\n        global _CURRENT_LAMBDA_CONTEXT\n        _CURRENT_LAMBDA_CONTEXT = context\n        try:\n            result = f(event, context)\n            return wait(lambda: result)\n        except:\n            cls, exc, trace = sys.exc_info()\n            report_exc_info((cls, exc, trace.tb_next))\n            wait()\n            raise\n    return wrapper\n\n\ndef report_exc_info(exc_info=None, request=None, extra_data=None, payload_data=None, level=None, **kw):\n    \"\"\"\n    Reports an exception to Rollbar, using exc_info (from calling sys.exc_info())\n\n    exc_info: optional, should be the result of calling sys.exc_info(). If omitted, sys.exc_info() will be called here.\n    request: optional, a WebOb, Werkzeug-based or Sanic request object.\n    extra_data: optional, will be included in the 'custom' section of the payload\n    payload_data: optional, dict that will override values in the final payload\n                  (e.g. 'level' or 'fingerprint')\n    kw: provided for legacy purposes; unused.\n\n    Example usage:\n\n    rollbar.init(access_token='YOUR_PROJECT_ACCESS_TOKEN')\n    try:\n        do_something()\n    except:\n        rollbar.report_exc_info(sys.exc_info(), request, {'foo': 'bar'}, {'level': 'warning'})\n    \"\"\"\n    if exc_info is None:\n        exc_info = sys.exc_info()\n\n    try:\n        return _report_exc_info(exc_info, request, extra_data, payload_data, level=level)\n    except Exception as e:\n        log.exception(\"Exception while reporting exc_info to Rollbar. %r\", e)\n\n\ndef report_message(message, level='error', request=None, extra_data=None, payload_data=None):\n    \"\"\"\n    Reports an arbitrary string message to Rollbar.\n\n    message: the string body of the message\n    level: level to report at. One of: 'critical', 'error', 'warning', 'info', 'debug'\n    request: the request object for the context of the message\n    extra_data: optional, will be included in the 'custom' section of the payload\n    payload_data: param names to pass in the 'data' level of the payload; overrides defaults.\n    \"\"\"\n    try:\n        return _report_message(message, level, request, extra_data, payload_data)\n    except Exception as e:\n        log.exception(\"Exception while reporting message to Rollbar. %r\", e)\n\n\ndef send_payload(payload, access_token):\n    \"\"\"\n    Sends a payload object, (the result of calling _build_payload() + _serialize_payload()).\n    Uses the configured handler from SETTINGS['handler']\n\n    Available handlers:\n    - 'blocking': calls _send_payload() (which makes an HTTP request) immediately, blocks on it\n    - 'thread': starts a single-use thread that will call _send_payload(). returns immediately.\n    - 'async': calls _send_payload_async() (which makes an async HTTP request using default async handler)\n    - 'agent': writes to a log file to be processed by rollbar-agent\n    - 'tornado': calls _send_payload_tornado() (which makes an async HTTP request using tornado's AsyncHTTPClient)\n    - 'gae': calls _send_payload_appengine() (which makes a blocking call to Google App Engine)\n    - 'twisted': calls _send_payload_twisted() (which makes an async HTTP request using Twisted and Treq)\n    - 'httpx': calls _send_payload_httpx() (which makes an async HTTP request using HTTPX)\n    - 'thread_pool': uses a pool of worker threads to make HTTP requests off the main thread. Returns immediately.\n    \"\"\"\n    payload = events.on_payload(payload)\n    if payload is False:\n        return\n\n    if sys.version_info >= (3, 6):\n        from rollbar.lib._async import get_current_handler\n        handler = get_current_handler()\n    else:\n        handler = SETTINGS.get('handler')\n\n    if handler == 'twisted':\n        payload['data']['framework'] = 'twisted'\n\n    payload_str = _serialize_payload(payload)\n    if handler == 'blocking':\n        _send_payload(payload_str, access_token)\n    elif handler == 'agent':\n        agent_log.error(payload_str)\n    elif handler == 'tornado':\n        if TornadoAsyncHTTPClient is None:\n            log.error('Unable to find tornado')\n            return\n        _send_payload_tornado(payload_str, access_token)\n    elif handler == 'gae':\n        if AppEngineFetch is None:\n            log.error('Unable to find AppEngine URLFetch module')\n            return\n        _send_payload_appengine(payload_str, access_token)\n    elif handler == 'twisted':\n        if treq is None:\n            log.error('Unable to find Treq')\n            return\n        _send_payload_twisted(payload_str, access_token)\n    elif handler == 'httpx':\n        if httpx is None:\n            log.error('Unable to find HTTPX')\n            return\n        _send_payload_httpx(payload_str, access_token)\n    elif handler == 'async':\n        if AsyncHTTPClient is None:\n            log.error('Unable to find async handler')\n            return\n        _send_payload_async(payload_str, access_token)\n    elif handler == 'thread':\n        _send_payload_thread(payload_str, access_token)\n    elif handler == 'thread_pool':\n        _send_payload_thread_pool(payload_str, access_token)\n    else:\n        # default to 'thread'\n        _send_payload_thread(payload_str, access_token)\n\n\ndef search_items(title, return_fields=None, access_token=None, endpoint=None, **search_fields):\n    \"\"\"\n    Searches a project for items that match the input criteria.\n\n    title: all or part of the item's title to search for.\n    return_fields: the fields that should be returned for each item.\n            e.g. ['id', 'project_id', 'status'] will return a dict containing\n                 only those fields for each item.\n    access_token: a project access token. If this is not provided,\n                  the one provided to init() will be used instead.\n    search_fields: additional fields to include in the search.\n            currently supported: status, level, environment\n    \"\"\"\n    if not title:\n        return []\n\n    if return_fields is not None:\n        return_fields = ','.join(return_fields)\n\n    return _get_api('search/',\n                    title=title,\n                    fields=return_fields,\n                    access_token=access_token,\n                    endpoint=endpoint,\n                    **search_fields)\n\n\ndef wait(f=None):\n    _threads.join()\n    if f is not None:\n        return f()\n\n\nclass ApiException(Exception):\n    \"\"\"\n    This exception will be raised if there was a problem decoding the\n    response from an API call.\n    \"\"\"\n    pass\n\n\nclass ApiError(ApiException):\n    \"\"\"\n    This exception will be raised if the API response contains an 'err'\n    field, denoting there was a problem fulfilling the api request.\n    \"\"\"\n    pass\n\n\nclass Result(object):\n    \"\"\"\n    This class encapsulates the response from an API call.\n    Usage:\n\n        result = search_items(title='foo', fields=['id'])\n        print result.data\n    \"\"\"\n\n    def __init__(self, access_token, path, params, data):\n        self.access_token = access_token\n        self.path = path\n        self.params = params\n        self.data = data\n\n    def __str__(self):\n        return str(self.data)\n\n\nclass PagedResult(Result):\n    \"\"\"\n    This class wraps the response from an API call that responded with\n    a page of results.\n    Usage:\n\n        result = search_items(title='foo', fields=['id'])\n        print 'First page: %d, data: %s' % (result.page, result.data)\n        result = result.next_page()\n        print 'Second page: %d, data: %s' % (result.page, result.data)\n    \"\"\"\n    def __init__(self, access_token, path, page_num, params, data, endpoint=None):\n        super(PagedResult, self).__init__(access_token, path, params, data)\n        self.page = page_num\n        self.endpoint = endpoint\n\n    def next_page(self):\n        params = copy.copy(self.params)\n        params['page'] = self.page + 1\n        return _get_api(self.path, endpoint=self.endpoint, **params)\n\n    def prev_page(self):\n        if self.page <= 1:\n            return self\n        params = copy.copy(self.params)\n        params['page'] = self.page - 1\n        return _get_api(self.path, endpoint=self.endpoint, **params)\n\n\n## internal functions\n\n\ndef _resolve_exception_class(idx, filter):\n    cls, level = filter\n    if isinstance(cls, str):\n        # Lazily resolve class name\n        parts = cls.split('.')\n        module = '.'.join(parts[:-1])\n        if module in sys.modules and hasattr(sys.modules[module], parts[-1]):\n            cls = getattr(sys.modules[module], parts[-1])\n            SETTINGS['exception_level_filters'][idx] = (cls, level)\n        else:\n            cls = None\n    return cls, level\n\n\ndef _filtered_level(exception):\n    for i, filter in enumerate(SETTINGS['exception_level_filters']):\n        cls, level = _resolve_exception_class(i, filter)\n        if cls and isinstance(exception, cls):\n            return level\n\n    return None\n\n\ndef _is_ignored(exception):\n    return _filtered_level(exception) == 'ignored'\n\n\ndef _create_agent_log():\n    \"\"\"\n    Creates .rollbar log file for use with rollbar-agent\n    \"\"\"\n    log_file = SETTINGS['agent.log_file']\n    if not log_file.endswith('.rollbar'):\n        log.error(\"Provided agent log file does not end with .rollbar, which it must. \"\n                  \"Using default instead.\")\n        log_file = DEFAULTS['agent.log_file']\n\n    retval = logging.getLogger('rollbar_agent')\n    handler = logging.FileHandler(log_file, 'a', 'utf-8')\n    formatter = logging.Formatter('%(message)s')\n    handler.setFormatter(formatter)\n    retval.addHandler(handler)\n    retval.setLevel(logging.WARNING)\n    return retval\n\n\ndef _report_exc_info(exc_info, request, extra_data, payload_data, level=None):\n    \"\"\"\n    Called by report_exc_info() wrapper\n    \"\"\"\n\n    if not _check_config():\n        return\n\n    filtered_level = _filtered_level(exc_info[1])\n    if level is None:\n        level = filtered_level\n\n    filtered_exc_info = events.on_exception_info(exc_info,\n                                                 request=request,\n                                                 extra_data=extra_data,\n                                                 payload_data=payload_data,\n                                                 level=level)\n\n    if filtered_exc_info is False:\n        return\n\n    cls, exc, trace = filtered_exc_info\n\n    data = _build_base_data(request)\n    if level is not None:\n        data['level'] = level\n\n    # walk the trace chain to collect cause and context exceptions\n    trace_chain = _walk_trace_chain(cls, exc, trace)\n\n    extra_trace_data = None\n    if len(trace_chain) > 1:\n        data['body'] = {\n            'trace_chain': trace_chain\n        }\n        if payload_data and ('body' in payload_data) and ('trace' in payload_data['body']):\n            extra_trace_data = payload_data['body']['trace']\n            del payload_data['body']['trace']\n    else:\n        data['body'] = {\n            'trace': trace_chain[0]\n        }\n\n    if extra_data:\n        extra_data = extra_data\n        if not isinstance(extra_data, dict):\n            extra_data = {'value': extra_data}\n        if extra_trace_data:\n            extra_data = dict_merge(extra_data, extra_trace_data, silence_errors=True)\n        data['custom'] = extra_data\n    if extra_trace_data and not extra_data:\n        data['custom'] = extra_trace_data\n\n    request = _get_actual_request(request)\n    _add_request_data(data, request)\n    _add_person_data(data, request)\n    _add_lambda_context_data(data)\n    _add_session_data(data)\n    data['server'] = _build_server_data()\n\n    if payload_data:\n        data = dict_merge(data, payload_data, silence_errors=True)\n\n    payload = _build_payload(data)\n    send_payload(payload, payload.get('access_token'))\n\n    return data['uuid']\n\n\ndef _walk_trace_chain(cls, exc, trace):\n    trace_chain = [_trace_data(cls, exc, trace)]\n\n    seen_exceptions = {exc}\n\n    while True:\n        exc = getattr(exc, '__cause__', None) or getattr(exc, '__context__', None)\n        if not exc:\n            break\n        trace_chain.append(_trace_data(type(exc), exc, getattr(exc, '__traceback__', None)))\n        if exc in seen_exceptions:\n            break\n        seen_exceptions.add(exc)\n\n    return trace_chain\n\n\ndef _trace_data(cls, exc, trace):\n    # exception info\n    # most recent call last\n    raw_frames = traceback.extract_tb(trace)\n    frames = [{'filename': f[0], 'lineno': f[1], 'method': f[2], 'code': f[3]} for f in raw_frames]\n\n    trace_data = {\n        'frames': frames,\n        'exception': {\n            'class': getattr(cls, '__name__', cls.__class__.__name__),\n            'message': str(exc),\n        }\n    }\n\n    _add_locals_data(trace_data, (cls, exc, trace))\n\n    return trace_data\n\n\ndef _report_message(message, level, request, extra_data, payload_data):\n    \"\"\"\n    Called by report_message() wrapper\n    \"\"\"\n    if not _check_config():\n        return\n\n    filtered_message = events.on_message(message,\n                                         request=request,\n                                         extra_data=extra_data,\n                                         payload_data=payload_data,\n                                         level=level)\n\n    if filtered_message is False:\n        return\n\n    data = _build_base_data(request, level=level)\n\n    # message\n    data['body'] = {\n        'message': {\n            'body': filtered_message\n        }\n    }\n\n    if extra_data:\n        extra_data = extra_data\n        data['body']['message'].update(extra_data)\n        data['custom'] = extra_data\n\n    request = _get_actual_request(request)\n    _add_request_data(data, request)\n    _add_person_data(data, request)\n    _add_lambda_context_data(data)\n    _add_session_data(data)\n    data['server'] = _build_server_data()\n\n    if payload_data:\n        data = dict_merge(data, payload_data, silence_errors=True)\n\n    payload = _build_payload(data)\n    send_payload(payload, payload.get('access_token'))\n\n    return data['uuid']\n\n\ndef _add_session_data(data: dict) -> None:\n    \"\"\"\n    Adds session data to the payload data if it can be found in the current session or request.\n    \"\"\"\n    session_data = get_current_session()\n    if session_data:\n        _add_session_attributes(data, session_data)\n        return\n\n    request = _session_data_from_request(data)\n    if request is None:\n        return\n    session_data = parse_session_request_baggage_headers(request.get('headers', None))\n\n    if session_data:\n        _add_session_attributes(data, session_data)\n\n\ndef _add_session_attributes(data: dict, session_data: list[Attribute]) -> None:\n    \"\"\"\n    Adds session attributes to the payload data. This function is careful to not overwrite any existing data in the\n    payload.\n    \"\"\"\n    if 'attributes' not in data:\n        data['attributes'] = session_data\n        return\n\n    existing_keys = {a['key'] for a in data['attributes']}\n\n    for attribute in session_data:\n        if attribute['key'] not in existing_keys:\n            data['attributes'].append(attribute)\n\n\ndef _session_data_from_request(data: dict) -> dict:\n    \"\"\"\n    Tries to find session data in the request object. Use the request object if provided, otherwise check the data as\n    it may already contain the request object. This is true for some frameworks (e.g. Django).\n    \"\"\"\n    if data is not None and 'request' in data:\n        return data.get('request', None)\n    return _get_actual_request(_build_request_data(get_request()))\n\n\ndef _check_config():\n    if not SETTINGS.get('enabled'):\n        log.info(\"pyrollbar: Not reporting because rollbar is disabled.\")\n        return False\n\n    # skip access token check for the agent handler\n    if SETTINGS.get('handler') == 'agent':\n        return True\n\n    # make sure we have an access_token\n    if not SETTINGS.get('access_token'):\n        log.warning(\"pyrollbar: No access_token provided. Please configure by calling rollbar.init() with your access token.\")\n        return False\n\n    return True\n\n\ndef _build_base_data(request, level='error'):\n    data = {\n        'timestamp': int(time.time()),\n        'environment': SETTINGS['environment'],\n        'level': level,\n        'language': 'python %s' % '.'.join(str(x) for x in sys.version_info[:3]),\n        'notifier': SETTINGS['notifier'],\n        'uuid': str(uuid.uuid4()),\n    }\n\n    if SETTINGS.get('code_version'):\n        data['code_version'] = SETTINGS['code_version']\n\n    if BASE_DATA_HOOK:\n        BASE_DATA_HOOK(request, data)\n\n    return data\n\n\ndef _add_person_data(data, request):\n    try:\n        person_data = _build_person_data(request)\n    except Exception as e:\n        log.exception(\"Exception while building person data for Rollbar payload: %r\", e)\n    else:\n        if person_data:\n            if not SETTINGS['capture_username'] and 'username' in person_data:\n                person_data['username'] = None\n            if not SETTINGS['capture_email'] and 'email' in person_data:\n                person_data['email'] = None\n            data['person'] = person_data\n\n\ndef _build_person_data(request):\n    \"\"\"\n    Returns a dictionary describing the logged-in user using data from `request`.\n\n    Try request.rollbar_person first, then 'user', then 'user_id'\n    \"\"\"\n    if hasattr(request, 'rollbar_person'):\n        rollbar_person_prop = request.rollbar_person\n        person = rollbar_person_prop() if callable(rollbar_person_prop) else rollbar_person_prop\n        if person and isinstance(person, dict):\n            return person\n        else:\n            return None\n\n    if StarletteRequest:\n        from rollbar.contrib.starlette.requests import hasuser\n    else:\n        def hasuser(request): return True\n\n    if hasuser(request) and hasattr(request, 'user'):\n        user_prop = request.user\n        user = user_prop() if callable(user_prop) else user_prop\n        if not user:\n            return None\n        elif isinstance(user, dict):\n            return user\n        else:\n            retval = {}\n            if getattr(user, 'id', None):\n                retval['id'] = str(user.id)\n            elif getattr(user, 'user_id', None):\n                retval['id'] = str(user.user_id)\n\n            # id is required, so only include username/email if we have an id\n            if retval.get('id'):\n                username = getattr(user, 'username', None)\n                email = getattr(user, 'email', None)\n                retval.update({\n                    'username': username,\n                    'email': email\n                })\n            return retval\n\n    if hasattr(request, 'user_id'):\n        user_id_prop = request.user_id\n        user_id = user_id_prop() if callable(user_id_prop) else user_id_prop\n        if not user_id:\n            return None\n        return {'id': str(user_id)}\n\n\ndef _get_func_from_frame(frame):\n    func_name = inspect.getframeinfo(frame).function\n    caller = frame.f_back\n    if caller:\n        func = caller.f_locals.get(func_name,\n                                   caller.f_globals.get(func_name))\n    else:\n        func = None\n\n    return func\n\n\ndef _add_locals_data(trace_data, exc_info):\n    if not SETTINGS['locals']['enabled']:\n        return\n\n    frames = trace_data['frames']\n\n    cur_tb = exc_info[2]\n    frame_num = 0\n    num_frames = len(frames)\n    while cur_tb:\n        cur_frame = frames[frame_num]\n        tb_frame = cur_tb.tb_frame\n        cur_tb = cur_tb.tb_next\n\n        if not isinstance(tb_frame, types.FrameType):\n            # this can happen if the traceback or frame is wrapped in some way,\n            # for example by `ExceptionInfo` in\n            # https://github.com/celery/billiard/blob/master/billiard/einfo.py\n            log.warning('Traceback frame not a types.FrameType. Ignoring.')\n            frame_num += 1\n            continue\n\n        # Create placeholders for argspec/varargspec/keywordspec/locals\n        argspec = None\n        varargspec = None\n        keywordspec = None\n        _locals = {}\n\n        try:\n            arginfo = inspect.getargvalues(tb_frame)\n\n            # Optionally fill in locals for this frame\n            if arginfo.locals and _check_add_locals(cur_frame, frame_num, num_frames):\n                # Get all of the named args\n                argspec = arginfo.args\n\n                if arginfo.varargs is not None:\n                    varargspec = arginfo.varargs\n                    if SETTINGS['locals']['scrub_varargs']:\n                        temp_varargs = list(arginfo.locals[varargspec])\n                        for i, arg in enumerate(temp_varargs):\n                            temp_varargs[i] = REDACT_REF\n\n                        arginfo.locals[varargspec] = tuple(temp_varargs)\n\n                if arginfo.keywords is not None:\n                    keywordspec = arginfo.keywords\n\n                _locals.update(arginfo.locals.items())\n\n        except Exception:\n            log.exception('Error while extracting arguments from frame. Ignoring.')\n\n        # Finally, serialize each arg/kwarg/local separately so that we only report\n        # CircularReferences for each variable, instead of for the entire payload\n        # as would be the case if we serialized that payload in one-shot.\n        if argspec:\n            cur_frame['argspec'] = argspec\n        if varargspec:\n            cur_frame['varargspec'] = varargspec\n        if keywordspec:\n            cur_frame['keywordspec'] = keywordspec\n        if _locals:\n            try:\n                cur_frame['locals'] = {k: _serialize_frame_data(v) for k, v in _locals.items()}\n            except Exception:\n                log.exception('Error while serializing frame data.')\n\n        frame_num += 1\n\n\ndef _serialize_frame_data(data):\n    return transforms.transform(\n        data,\n        [_scrub_redact_transform, _serialize_transform],\n        batch_transforms=SETTINGS['batch_transforms']\n    )\n\n\ndef _add_lambda_context_data(data):\n    \"\"\"\n    Attempts to add information from the lambda context if it exists\n    \"\"\"\n    global _CURRENT_LAMBDA_CONTEXT\n    context = _CURRENT_LAMBDA_CONTEXT\n    if context is None:\n        return\n    try:\n        lambda_data = {\n            'lambda': {\n                'remaining_time_in_millis': context.get_remaining_time_in_millis(),\n                'function_name': context.function_name,\n                'function_version': context.function_version,\n                'arn': context.invoked_function_arn,\n                'request_id': context.aws_request_id,\n            }\n        }\n        if 'custom' in data:\n            data['custom'] = dict_merge(data['custom'], lambda_data, silence_errors=True)\n        else:\n            data['custom'] = lambda_data\n    except Exception as e:\n        log.exception(\"Exception while adding lambda context data: %r\", e)\n    finally:\n        _CURRENT_LAMBDA_CONTEXT = None\n\n\ndef _add_request_data(data, request):\n    \"\"\"\n    Attempts to build request data; if successful, sets the 'request' key on `data`.\n    \"\"\"\n    try:\n        request_data = _build_request_data(request)\n    except Exception as e:\n        log.exception(\"Exception while building request_data for Rollbar payload: %r\", e)\n    else:\n        if request_data:\n            _filter_ip(request_data, SETTINGS['capture_ip'])\n            data['request'] = request_data\n\n\ndef _check_add_locals(frame, frame_num, total_frames):\n    \"\"\"\n    Returns True if we should record local variables for the given frame.\n    \"\"\"\n    # Include the last frames locals\n    # Include any frame locals that came from a file in the project's root\n    root = SETTINGS.get('root')\n    if root:\n        # coerce to string, in case root is a Path object\n        root = str(root)\n    else:\n        root = ''\n    return any(((frame_num == total_frames - 1),\n                ('root' in SETTINGS and (frame.get('filename') or '').lower().startswith(root.lower()))))\n\n\ndef _get_actual_request(request):\n    if WerkzeugLocalProxy and isinstance(request, WerkzeugLocalProxy):\n        try:\n            actual_request = request._get_current_object()\n        except RuntimeError:\n            return None\n        return actual_request\n    return request\n\n\ndef _build_request_data(request):\n    \"\"\"\n    Returns a dictionary containing data from the request.\n    \"\"\"\n\n    # webob (pyramid)\n    if WebobBaseRequest and isinstance(request, WebobBaseRequest):\n        return _build_webob_request_data(request)\n\n    # django\n    if DjangoHttpRequest and isinstance(request, DjangoHttpRequest):\n        return _build_django_request_data(request)\n\n    # django rest framework\n    if RestFrameworkRequest and isinstance(request, RestFrameworkRequest):\n        return _build_django_request_data(request)\n\n    # werkzeug (flask)\n    if WerkzeugRequest and isinstance(request, WerkzeugRequest):\n        return _build_werkzeug_request_data(request)\n\n    # tornado\n    if TornadoRequest and isinstance(request, TornadoRequest):\n        return _build_tornado_request_data(request)\n\n    # bottle\n    if BottleRequest and isinstance(request, BottleRequest):\n        return _build_bottle_request_data(request)\n\n    # Sanic\n    if SanicRequest and isinstance(request, SanicRequest):\n        return _build_sanic_request_data(request)\n\n    # falcon\n    if FalconRequest and isinstance(request, FalconRequest):\n        return _build_falcon_request_data(request)\n\n    # Plain wsgi (should be last)\n    if isinstance(request, dict) and 'wsgi.version' in request:\n        return _build_wsgi_request_data(request)\n\n    # FastAPI (built on top of Starlette, so keep the order)\n    if FastAPIRequest and isinstance(request, FastAPIRequest):\n        return _build_fastapi_request_data(request)\n\n    # Starlette (should be the last one for Starlette based frameworks)\n    if StarletteRequest and isinstance(request, StarletteRequest):\n        return _build_starlette_request_data(request)\n\n    return None\n\n\ndef _build_webob_request_data(request):\n    request_data = {\n        'url': request.url,\n        'GET': dict(request.GET),\n        'user_ip': _extract_user_ip(request),\n        'headers': dict(request.headers),\n        'method': request.method,\n    }\n\n    try:\n        if request.json:\n            request_data['json'] = request.json\n    except:\n        pass\n\n    # pyramid matchdict\n    if getattr(request, 'matchdict', None):\n        request_data['params'] = request.matchdict\n\n    # workaround for webob bug when the request body contains binary data but has a text\n    # content-type\n    try:\n        request_data['POST'] = dict(request.POST)\n    except UnicodeDecodeError:\n        request_data['body'] = request.body\n\n    return request_data\n\n\ndef _extract_wsgi_headers(items):\n    headers = {}\n    for k, v in items:\n        if k.startswith('HTTP_'):\n            header_name = '-'.join(k[len('HTTP_'):].replace('_', ' ').title().split(' '))\n            headers[header_name] = v\n    return headers\n\n\ndef _build_django_request_data(request):\n    url = request.build_absolute_uri()\n\n    request_data = {\n        'url': url,\n        'method': request.method,\n        'GET': dict(request.GET),\n        'POST': dict(request.POST),\n        'user_ip': _wsgi_extract_user_ip(request.META),\n    }\n\n    if SETTINGS['include_request_body']:\n        try:\n            request_data['body'] = request.body\n        except:\n            pass\n\n    request_data['headers'] = _extract_wsgi_headers(request.META.items())\n\n    return request_data\n\n\ndef _build_werkzeug_request_data(request):\n    request_data = {\n        'url': request.url,\n        'GET': dict(request.args),\n        'POST': dict(request.form),\n        'user_ip': _extract_user_ip(request),\n        'headers': dict(request.headers),\n        'method': request.method,\n        'files_keys': list(request.files.keys()),\n    }\n\n    if SETTINGS['include_request_body']:\n        try:\n            if request.json:\n                request_data['body'] = request.json\n        except Exception:\n            pass\n\n    return request_data\n\n\ndef _build_tornado_request_data(request):\n    request_data = {\n        'url': request.full_url(),\n        'user_ip': request.remote_ip,\n        'headers': dict(request.headers),\n        'method': request.method,\n        'files_keys': request.files.keys(),\n        'start_time': getattr(request, '_start_time', None),\n    }\n    request_data[request.method] = request.arguments\n\n    return request_data\n\n\ndef _build_bottle_request_data(request):\n    request_data = {\n        'url': request.url,\n        'user_ip': request.remote_addr,\n        'headers': dict(request.headers),\n        'method': request.method,\n        'GET': dict(request.query)\n    }\n\n\n    if SETTINGS['include_request_body']:\n        if request.json:\n            try:\n                request_data['body'] = request.body.getvalue()\n            except:\n                pass\n        else:\n            request_data['POST'] = dict(request.forms)\n\n    return request_data\n\n\ndef _build_sanic_request_data(request):\n    request_data = {\n        'url': request.url,\n        'user_ip': request.remote_addr,\n        'headers': request.headers,\n        'method': request.method,\n        'GET': dict(request.args)\n    }\n\n    if SETTINGS['include_request_body']:\n        if request.json:\n            try:\n                request_data['body'] = request.json\n            except:\n                pass\n        else:\n            request_data['POST'] = request.form\n\n    return request_data\n\n\ndef _build_falcon_request_data(request):\n    request_data = {\n        'url': request.url,\n        'user_ip': _wsgi_extract_user_ip(request.env),\n        'headers': dict(request.headers),\n        'method': request.method,\n        'GET': dict(request.params),\n        'context': dict(request.context),\n    }\n\n    return request_data\n\n\ndef _build_wsgi_request_data(request):\n    request_data = {\n        'url': wsgiref.util.request_uri(request),\n        'user_ip': _wsgi_extract_user_ip(request),\n        'method': request.get('REQUEST_METHOD'),\n    }\n    if 'QUERY_STRING' in request:\n        request_data['GET'] = parse_qs(request['QUERY_STRING'], keep_blank_values=True)\n        # Collapse single item arrays\n        request_data['GET'] = {k: (v[0] if len(v) == 1 else v) for k, v in request_data['GET'].items()}\n\n    request_data['headers'] = _extract_wsgi_headers(request.items())\n\n    if SETTINGS['include_request_body']:\n        try:\n            length = int(request.get('CONTENT_LENGTH', 0))\n        except ValueError:\n            length = 0\n        input = request.get('wsgi.input')\n        if length and input and hasattr(input, 'seek') and hasattr(input, 'tell'):\n            pos = input.tell()\n            input.seek(0, 0)\n            request_data['body'] = input.read(length)\n            input.seek(pos, 0)\n\n    return request_data\n\ndef _build_starlette_request_data(request):\n    from starlette.datastructures import UploadFile\n\n    request_data = {\n        'url': str(request.url),\n        'GET': dict(request.query_params),\n        'headers': dict(request.headers),\n        'method': request.method,\n        'user_ip': _starlette_extract_user_ip(request),\n        'params': dict(request.path_params),\n    }\n\n    if hasattr(request, '_form') and request._form is not None:\n        request_data['POST'] = {\n            k: v.filename if isinstance(v, UploadFile) else v\n            for k, v in request._form.items()\n        }\n        request_data['files_keys'] = [\n            field.filename\n            for field in request._form.values()\n            if isinstance(field, UploadFile)\n        ]\n\n    if hasattr(request, '_body'):\n        body = request._body.decode()\n    else:\n        body = None\n\n    if body and SETTINGS['include_request_body']:\n        request_data['body'] = body\n\n    if hasattr(request, '_json'):\n        request_data['json'] = request._json\n    elif body:\n        try:\n            request_data['json'] = json.loads(body)\n        except json.JSONDecodeError:\n            pass\n\n    # Filter out empty values\n    request_data = {k: v for k, v in request_data.items() if v}\n\n    return request_data\n\ndef _build_fastapi_request_data(request):\n    return _build_starlette_request_data(request)\n\n\ndef _filter_ip(request_data, capture_ip):\n    if 'user_ip' not in request_data or capture_ip == True:\n        return\n\n    current_ip = request_data['user_ip']\n    if not current_ip:\n        return\n\n    new_ip = current_ip\n    if not capture_ip:\n        new_ip = None\n    elif capture_ip == ANONYMIZE:\n        try:\n            if '.' in current_ip:\n                new_ip = '.'.join(current_ip.split('.')[0:3]) + '.0'\n            elif ':' in current_ip:\n                parts = current_ip.split(':')\n                if len(parts) > 2:\n                    terminal = '0000:0000:0000:0000:0000'\n                    new_ip = ':'.join(parts[0:3] + [terminal])\n            else:\n                new_ip = None\n        except:\n            new_ip = None\n\n    request_data['user_ip'] = new_ip\n\n\ndef _build_server_data():\n    \"\"\"\n    Returns a dictionary containing information about the server environment.\n    \"\"\"\n    # server environment\n    host = SETTINGS.get('host') or socket.gethostname()\n    server_data = {\n        'host': host,\n        'pid': os.getpid()\n    }\n\n    # argv does not always exist in embedded python environments\n    argv = getattr(sys, 'argv', None)\n    if argv:\n         server_data['argv'] = argv\n\n    for key in ['branch', 'root']:\n        if SETTINGS.get(key):\n            server_data[key] = SETTINGS[key]\n\n    return server_data\n\n\ndef _transform(obj, key=None):\n    return transforms.transform(\n        obj,\n        _transforms,\n        key=key,\n        batch_transforms=SETTINGS['batch_transforms']\n    )\n\n\ndef _build_payload(data):\n    \"\"\"\n    Returns the full payload as a string.\n    \"\"\"\n\n    for k, v in data.items():\n        data[k] = _transform(v, key=(k,))\n\n    payload = {\n        'access_token': SETTINGS['access_token'],\n        'data': data\n    }\n\n    return payload\n\n\ndef _serialize_payload(payload):\n    return json.dumps(payload, default=defaultJSONEncode)\n\n\ndef _send_payload(payload_str, access_token):\n    try:\n        _post_api('item/', payload_str, access_token=access_token)\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n    try:\n        _threads.get_nowait()\n        _threads.task_done()\n    except queue.Empty:\n        pass\n\n\ndef _send_payload_thread(payload_str, access_token):\n    thread = threading.Thread(target=_send_payload, args=(payload_str, access_token))\n    _threads.put(thread)\n    thread.start()\n\n\ndef _send_payload_pool(payload_str, access_token):\n    try:\n        _post_api('item/', payload_str, access_token=access_token)\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n\n\ndef _send_payload_thread_pool(payload_str, access_token):\n    from rollbar.lib.thread_pool import submit\n    submit(_send_payload_pool, payload_str, access_token)\n\n\ndef _send_payload_appengine(payload_str, access_token):\n    try:\n        _post_api_appengine('item/', payload_str, access_token=access_token)\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n\n\ndef _post_api_appengine(path, payload_str, access_token=None):\n    headers = {'Content-Type': 'application/json'}\n\n    if access_token is not None:\n        headers['X-Rollbar-Access-Token'] = access_token\n\n    url = urljoin(SETTINGS['endpoint'], path)\n    resp = AppEngineFetch(url,\n                          method=\"POST\",\n                          payload=payload_str,\n                          headers=headers,\n                          allow_truncated=False,\n                          deadline=SETTINGS.get('timeout', DEFAULT_TIMEOUT),\n                          validate_certificate=SETTINGS.get('verify_https', True))\n\n    return _parse_response(path, SETTINGS['access_token'], payload_str, resp)\n\n\ndef _post_api(path, payload_str, access_token=None):\n    headers = {'Content-Type': 'application/json'}\n\n    if access_token is not None:\n        headers['X-Rollbar-Access-Token'] = access_token\n\n    url = urljoin(SETTINGS['endpoint'], path)\n    resp = transport.post(url,\n                          data=payload_str,\n                          headers=headers,\n                          timeout=SETTINGS.get('timeout', DEFAULT_TIMEOUT),\n                          verify=SETTINGS.get('verify_https', True),\n                          proxy=SETTINGS.get('http_proxy'),\n                          proxy_user=SETTINGS.get('http_proxy_user'),\n                          proxy_password=SETTINGS.get('http_proxy_password'))\n\n    return _parse_response(path, SETTINGS['access_token'], payload_str, resp)\n\n\ndef _get_api(path, access_token=None, endpoint=None, **params):\n    access_token = access_token or SETTINGS['access_token']\n    url = urljoin(endpoint or SETTINGS['endpoint'], path)\n    params['access_token'] = access_token\n    resp = transport.get(url,\n                         params=params,\n                         verify=SETTINGS.get('verify_https', True),\n                         proxy=SETTINGS.get('http_proxy'),\n                         proxy_user=SETTINGS.get('http_proxy_user'),\n                         proxy_password=SETTINGS.get('http_proxy_password'))\n    return _parse_response(path, access_token, params, resp, endpoint=endpoint)\n\n\ndef _send_payload_tornado(payload_str, access_token):\n    try:\n        _post_api_tornado('item/', payload_str, access_token=access_token)\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n\n\ndef _post_api_tornado(path, payload_str, access_token=None):\n    headers = {'Content-Type': 'application/json'}\n\n    if access_token is not None:\n        headers['X-Rollbar-Access-Token'] = access_token\n    else:\n        access_token = SETTINGS['access_token']\n\n    url = urljoin(SETTINGS['endpoint'], path)\n\n    def post_tornado_cb(resp):\n        r = requests.Response()\n        r._content = resp.body\n        r.status_code = resp.code\n        r.headers.update(resp.headers)\n        try:\n            _parse_response(path, access_token, payload_str, r)\n        except Exception as e:\n            log.exception('Exception while posting item %r', e)\n\n    TornadoAsyncHTTPClient().fetch(url,\n                                   callback=post_tornado_cb,\n                                   raise_error=False,\n                                   body=payload_str,\n                                   method='POST',\n                                   connect_timeout=SETTINGS.get('timeout', DEFAULT_TIMEOUT),\n                                   request_timeout=SETTINGS.get('timeout', DEFAULT_TIMEOUT))\n\n\ndef _send_payload_twisted(payload_str, access_token):\n    try:\n        _post_api_twisted('item/', payload_str, access_token=access_token)\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n\ndef _post_api_twisted(path, payload_str, access_token=None):\n    def post_data_cb(data, resp):\n        resp._content = data\n        _parse_response(path, SETTINGS['access_token'], payload_str, resp)\n\n    def post_cb(resp):\n        r = requests.Response()\n        r.status_code = resp.code\n        r.headers.update(resp.headers.getAllRawHeaders())\n        return treq.content(resp).addCallback(post_data_cb, r)\n\n    headers = {'Content-Type': ['application/json; charset=utf-8']}\n    if access_token is not None:\n        headers['X-Rollbar-Access-Token'] = [access_token]\n\n    url = urljoin(SETTINGS['endpoint'], path)\n    try:\n        encoded_payload = payload_str.encode('utf8')\n    except (UnicodeDecodeError, UnicodeEncodeError):\n        encoded_payload = payload_str\n\n    treq_client = treq.client.HTTPClient(Agent(reactor, contextFactory=VerifyHTTPS()))\n    d = treq_client.post(url, encoded_payload, headers=headers,\n                  timeout=SETTINGS.get('timeout', DEFAULT_TIMEOUT))\n    d.addCallback(post_cb)\n\ndef _send_payload_httpx(payload_str, access_token):\n    from rollbar.lib._async import call_later, _post_api_httpx\n    try:\n        call_later(_post_api_httpx('item/', payload_str,\n                                   access_token=access_token))\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n\n\n\ndef _send_payload_async(payload_str, access_token):\n    try:\n        _send_payload_httpx(payload_str, access_token=access_token)\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n\n\ndef _send_failsafe(message, uuid, host):\n    body_message = ('Failsafe from pyrollbar: {0}. Original payload may be found '\n                    'in your server logs by searching for the UUID.').format(message)\n\n    data = {\n        'level': 'error',\n        'environment': SETTINGS['environment'],\n        'body': {\n            'message': {\n                'body': body_message\n            }\n        },\n        'notifier': SETTINGS['notifier'],\n        'custom': {\n            'orig_uuid': uuid,\n            'orig_host': host\n        },\n        'failsafe': True,\n        'internal': True,\n    }\n\n    payload = _build_payload(data)\n\n    try:\n        send_payload(payload, SETTINGS['access_token'])\n    except Exception:\n        log.exception('Rollbar: Error sending failsafe.')\n\n\ndef _parse_response(path, access_token, params, resp, endpoint=None):\n    if isinstance(resp, requests.Response):\n        try:\n            data = resp.text\n        except Exception:\n            data = resp.content\n            log.error('resp.text is undefined, resp.content is %r', resp.content)\n    else:\n        data = resp.content\n\n    global _LAST_RESPONSE_STATUS\n    last_response_was_429 = _LAST_RESPONSE_STATUS == 429\n    _LAST_RESPONSE_STATUS = resp.status_code\n\n    if resp.status_code == 429:\n        if SETTINGS['log_all_rate_limited_items'] or not last_response_was_429:\n            log.warning(\"Rollbar: over rate limit, data was dropped.\")\n            if SETTINGS['log_payload_on_error']:\n                log.warning(\"Payload was: %r\", params)\n        return\n    elif resp.status_code == 502:\n        log.exception('Rollbar api returned a 502')\n        return\n    elif resp.status_code == 413:\n        uuid = None\n        host = None\n\n        try:\n            payload = json.loads(params)\n            uuid = payload['data']['uuid']\n            host = payload['data']['server']['host']\n            log.error(\"Rollbar: request entity too large for UUID %r\\n.\", uuid)\n            if SETTINGS['log_payload_on_error']:\n                log.error(\"Payload:\\n%r\", payload)\n        except (TypeError, ValueError):\n            log.exception('Unable to decode JSON for failsafe.')\n        except KeyError:\n            log.exception('Unable to find payload parameters for failsafe.')\n\n        _send_failsafe('payload too large', uuid, host)\n        # TODO: Should we return here?\n    elif resp.status_code != 200:\n        log.warning(\"Got unexpected status code from Rollbar api: %s\\nResponse:\\n%s\",\n                    resp.status_code, data)\n        # TODO: Should we also return here?\n\n    try:\n        json_data = json.loads(data)\n    except (TypeError, ValueError):\n        log.exception('Could not decode Rollbar api response:\\n%s', data)\n        raise ApiException('Request to %s returned invalid JSON response', path)\n    else:\n        if json_data.get('err'):\n            raise ApiError(json_data.get('message') or 'Unknown error')\n\n        result = json_data.get('result', {})\n\n        if 'page' in result:\n            return PagedResult(access_token, path, result['page'], params, result, endpoint=endpoint)\n        else:\n            return Result(access_token, path, params, result)\n\n\ndef _extract_user_ip_from_headers(request):\n    forwarded_for = request.headers.get('X-Forwarded-For')\n    if forwarded_for:\n        return forwarded_for\n    real_ip = request.headers.get('X-Real-Ip')\n    if real_ip:\n        return real_ip\n    return None\n\n\ndef _extract_user_ip(request):\n    return _extract_user_ip_from_headers(request) or request.remote_addr\n\n\ndef _wsgi_extract_user_ip(environ):\n    forwarded_for = environ.get('HTTP_X_FORWARDED_FOR')\n    if forwarded_for:\n        return forwarded_for\n    real_ip = environ.get('HTTP_X_REAL_IP')\n    if real_ip:\n        return real_ip\n    return environ['REMOTE_ADDR']\n\n\ndef _starlette_extract_user_ip(request):\n    if not hasattr(request, 'client'):\n        return _extract_user_ip_from_headers(request)\n    if not hasattr(request.client, 'host'):\n        return _extract_user_ip_from_headers(request)\n    return request.client.host or _extract_user_ip_from_headers(request)\n"
  },
  {
    "path": "rollbar/cli.py",
    "content": "import optparse\nimport sys\n\nimport rollbar\n\nVERSION = '0.1'\n\nverbose = False\n\ndef _gen_report_message(level):\n    def _wrapped(lines):\n        line_data = '\\n'.join(lines)\n        if verbose:\n            print('Rollbar [%s]: %s' % (level, line_data))\n        rollbar.report_message(line_data, level=level, extra_data={'cli_version': VERSION})\n    return _wrapped\n\n\nCMDS = {\n    'debug': _gen_report_message('debug'),\n    'info': _gen_report_message('info'),\n    'warning': _gen_report_message('warning'),\n    'error': _gen_report_message('error'),\n    'critical': _gen_report_message('critical'),\n}\n\ndef main():\n    global verbose\n    parser = optparse.OptionParser(version='%prog version ' + VERSION)\n    parser.add_option('-t', '--access_token',\n                      dest='access_token',\n                      help=\"You project's access token from rollbar.com.\",\n                      metavar='ACCESS_TOKEN')\n    parser.add_option('-e', '--environment',\n                      dest='environment',\n                      help=\"The environment to report errors and messages to. \\\n                      Can be any string; suggestions: 'production', 'development', 'staging'\",\n                      metavar='ENVIRONMENT')\n    parser.add_option('-u', '--url',\n                      dest='endpoint_url',\n                      help=\"The Rollbar API endpoint url to send data to.\",\n                      metavar='ENDPOINT_URL',\n                      default=rollbar.DEFAULT_ENDPOINT)\n    parser.add_option('-m', '--handler',\n                      dest='handler',\n                      help=\"The method in which to report errors.\",\n                      metavar='HANDLER',\n                      choices=[\"thread\", \"blocking\", \"agent\"],\n                      default=\"blocking\")\n    parser.add_option('-v', '--verbose',\n                      dest='verbose',\n                      help=\"Print verbose output.\",\n                      action='store_true',\n                      default=False)\n\n    options, args = parser.parse_args()\n\n    access_token = options.access_token\n    env = options.environment\n    endpoint = options.endpoint_url\n    handler = options.handler\n    verbose = options.verbose\n\n    if not access_token:\n        parser.error('missing access_token')\n    if not env:\n        parser.error('missing environment')\n\n    rollbar.init(access_token, environment=env, endpoint=endpoint, handler=handler)\n\n    def _do_cmd(cmd_name, line):\n        cmd = CMDS.get(cmd_name.lower())\n        if cmd:\n            cmd([line])\n            return True\n\n        return False\n\n    if len(args) > 1:\n        sent = _do_cmd(args[0], ' '.join(args[1:]))\n        sys.exit(0 if sent else 1)\n\n    cur_cmd_name = None\n    try:\n        cur_line = sys.stdin.readline()\n        while cur_line:\n            cur_line = cur_line.strip()\n            parts = cur_line.split(' ')\n\n            if parts:\n                cur_cmd_name = parts[0]\n                parts = parts[1:]\n                _do_cmd(cur_cmd_name, ' '.join(parts))\n\n            cur_line = sys.stdin.readline()\n    except (KeyboardInterrupt, SystemExit) as e:\n        pass\n"
  },
  {
    "path": "rollbar/contrib/__init__.py",
    "content": ""
  },
  {
    "path": "rollbar/contrib/asgi/__init__.py",
    "content": "__all__ = ['ReporterMiddleware']\n\nfrom .middleware import ReporterMiddleware\n"
  },
  {
    "path": "rollbar/contrib/asgi/integration.py",
    "content": "import inspect\nimport functools\n\nimport rollbar\n\n\nclass IntegrationBase:\n    \"\"\"\n    Superclass for class integrations.\n    \"\"\"\n\n    def __init__(self):\n        if hasattr(self, '_integrate'):\n            self._integrate()\n\n\nclass integrate:\n    \"\"\"\n    Integrates functions and classes (derived from IntegrationBase) in the SDK.\n    \"\"\"\n\n    def __init__(self, *, framework_name='unknown'):\n        self._framework_name = framework_name\n\n    def __call__(self, obj):\n        if inspect.isclass(obj):\n            obj._integrate = self._register_hook\n            return obj\n        else:\n            return self._insert_hook(obj)\n\n    def _register_hook(self):\n        def _hook(request, data):\n            data['framework'] = self._framework_name\n\n        rollbar.BASE_DATA_HOOK = _hook\n\n    def _insert_hook(self, func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            self._register_hook()\n            return func(*args, **kwargs)\n\n        return wrapper\n"
  },
  {
    "path": "rollbar/contrib/asgi/middleware.py",
    "content": "import logging\nimport sys\nfrom typing import Iterable\n\nimport rollbar\nfrom .integration import IntegrationBase, integrate\nfrom .types import ASGIApp, Receive, Scope, Send\nfrom rollbar.lib._async import RollbarAsyncError, try_report\nfrom rollbar.lib.session import set_current_session, reset_current_session\n\nlog = logging.getLogger(__name__)\n\n\n@integrate(framework_name='asgi')\nclass ReporterMiddleware(IntegrationBase):\n    def __init__(self, app: ASGIApp) -> None:\n        super().__init__()\n\n        self.app = app\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        if scope['type'] == 'http':\n            set_current_session(self._format_headers(scope['headers']))\n        try:\n            await self.app(scope, receive, send)\n        except Exception:\n            if scope['type'] == 'http':\n                exc_info = sys.exc_info()\n\n                try:\n                    await try_report(exc_info)\n                except RollbarAsyncError:\n                    log.warning(\n                        'Failed to report asynchronously. Trying to report synchronously.'\n                    )\n                    rollbar.report_exc_info(exc_info)\n            raise\n        finally:\n            if scope['type'] == 'http':\n                reset_current_session()\n\n    @staticmethod\n    def _format_headers(headers: Iterable[tuple[bytes, bytes]]) -> dict[str, str]:\n        \"\"\"\n        Convert list of header tuples to a dictionary with string keys and values.\n\n        Headers are expected to be in the format: [(b'header-name', b'header-value'), ...]\n        \"\"\"\n        return {key.decode('latin-1'): value.decode('latin-1') for key, value in headers}"
  },
  {
    "path": "rollbar/contrib/asgi/types.py",
    "content": "# Copyright © 2018, [Encode OSS Ltd](https://www.encode.io/).\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n#\n# * Redistributions of source code must retain the above copyright notice, this\n#   list of conditions and the following disclaimer.\n#\n# * Redistributions in binary form must reproduce the above copyright notice,\n#   this list of conditions and the following disclaimer in the documentation\n#   and/or other materials provided with the distribution.\n#\n# * Neither the name of the copyright holder nor the names of its\n#   contributors may be used to endorse or promote products derived from\n#   this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport typing\n\nScope = typing.MutableMapping[str, typing.Any]\nMessage = typing.MutableMapping[str, typing.Any]\n\nReceive = typing.Callable[[], typing.Awaitable[Message]]\nSend = typing.Callable[[Message], typing.Awaitable[None]]\n\nASGIApp = typing.Callable[[Scope, Receive, Send], typing.Awaitable[None]]\n"
  },
  {
    "path": "rollbar/contrib/bottle/__init__.py",
    "content": "import bottle, rollbar, sys\n\nclass RollbarBottleReporter(object):\n    '''\n    A Bottle plugin that reports errors to Rollbar\n    All args and kwargs are passed to `rollbar.init`\n    '''\n    name = 'rollbar-bottle-reporter'\n    api = 2\n\n    def __init__(self, *args, **kwargs):\n        if 'exception_level_filters' in kwargs:\n            kwargs['exception_level_filters'].append((bottle.BaseResponse, 'ignored'))\n        else:\n            kwargs['exception_level_filters'] = [(bottle.BaseResponse, 'ignored')]\n\n        rollbar.init(*args, **kwargs)\n\n        def hook(request, data):\n            data['framework'] = 'bottle'\n\n            if request:\n                route = request['bottle.route']\n                data['context'] = route.name or route.rule\n\n        rollbar.BASE_DATA_HOOK = hook\n\n    def __call__(self, callback):\n        def wrapper(*args, **kwargs):\n            try:\n                return callback(*args, **kwargs)\n            except Exception as e:\n                rollbar.report_exc_info(sys.exc_info(), request=bottle.request)\n                raise\n\n        return wrapper\n\n\n"
  },
  {
    "path": "rollbar/contrib/django/__init__.py",
    "content": ""
  },
  {
    "path": "rollbar/contrib/django/context_processors.py",
    "content": "\"\"\"\ndjango-rollbar context processor\n\nTo install, add the following in your settings.py:\n1. add 'rollbar.contrib.django.context_processors.rollbar_settings' to TEMPLATE_CONTEXT_PROCESSORS\n2. add a section like this:\nROLLBAR = {\n    'client_access_token': 'tokengoeshere',\n}\n3. you can now access your rollbar settings as rollbar_settings from within your django templates\n\nSee README.rst for full installation and configuration instructions.\n\"\"\"\n\nfrom django.conf import settings\n\n\ndef rollbar_settings(request):\n    \"\"\"Grabs the rollbar settings to make them available to templates.\"\"\"\n    if not hasattr(settings, 'ROLLBAR'):\n        return {}\n    return {'rollbar_settings': settings.ROLLBAR}\n"
  },
  {
    "path": "rollbar/contrib/django/middleware.py",
    "content": "r\"\"\"\ndjango-rollbar middleware\n\nThere are two options for installing the Rollbar middleware. Both options\nrequire modifying your settings.py file.\n\nThe first option is to use\n'rollbar.contrib.django.middleware.RollbarNotifierMiddleware' which will\nreport all exceptions to Rollbar including 404s. This middlware should be\nplaced as the last item in your middleware list which is:\n    * MIDDLEWARE_CLASSES in Django 1.9 and earlier\n    * MIDDLEWARE in Django 1.10 and up\n\nThe other option is two use the two separate middlewares:\n    * 'rollbar.contrib.django.middleware.RollbarNotifierMiddlewareExcluding404'\n    * 'rollbar.contrib.django.middleware.RollbarNotifierMiddlewareOnly404'\nThe Excluding404 middleware should be placed as the last item in your middleware\nlist, and the Only404 middleware should be placed as the first item in your\nmiddleware list. This allows 404s to be processed by your other middlewares\nbefore sendind an item to Rollbar. Therefore if you handle the 404 differently\nin a way that returns a response early you won't end up with a Rollbar item.\n\nRegardless of which method you use, you also should add a section to settings.py\nlike this:\n\nROLLBAR = {\n    'access_token': 'tokengoeshere',\n}\n\nThis can be used for passing configuration options to Rollbar. Additionally,\nyou can use the key 'ignorable_404_urls' to set an iterable of regular expression\npatterns to use to determine whether a 404 exception should be ignored based\non the full url path for the request. For example,\n\nimport re\nROLLBAR = {\n    'access_token': 'YOUR_TOKEN',\n    'ignorable_404_urls': (\n        re.compile(r'/index\\.php'),\n        re.compile('/foobar'),\n    ),\n}\n\nTo get more control of middleware and enrich it with custom data\nyou can subclass any of the middleware classes described above\nand optionally override the methods:\n    def get_extra_data(self, request, exc):\n        ''' May be defined.  Must return a dict or None. Use it to put some custom extra data on rollbar event. '''\n        return\n\n    def get_payload_data(self, request, exc):\n        ''' May be defined.  Must return a dict or None. Use it to put some custom payload data on rollbar event. '''\n        return\nYou would then insert your custom subclass into your middleware\nconfiguration in the same place as the base class as described above.\nFor example:\n\n1. create a 'middleware.py' file on your project (name is up to you)\n2. import the rollbar default middleware: 'from rollbar.contrib.django.middleware import RollbarNotifierMiddleware'\n3. create your own middleware like this:\nclass CustomRollbarNotifierMiddleware(RollbarNotifierMiddleware):\n    def get_extra_data(self, request, exc):\n        ''' May be defined.  Must return a dict or None. Use it to put some custom extra data on rollbar event. '''\n        return\n\n    def get_payload_data(self, request, exc):\n        ''' May be defined.  Must return a dict or None. Use it to put some custom payload data on rollbar event. '''\n        return\n\n4. add 'path.to.your.CustomRollbarNotifierMiddleware' in your settings.py to\n    a. MIDDLEWARE_CLASSES in Django 1.9 and earlier\n    b. MIDDLEWARE in Django 1.10 and up\n5. add a section like this in your settings.py:\nROLLBAR = {\n    'access_token': 'tokengoeshere',\n}\n\nSee README.rst for full installation and configuration instructions.\n\"\"\"\n\nimport logging\nimport sys\n\nimport rollbar\n\nfrom django.core.exceptions import MiddlewareNotUsed\nfrom django.conf import settings\nfrom django.http import Http404\n\nfrom rollbar import set_current_session\nfrom rollbar.lib.session import reset_current_session\n\ntry:\n    from django.urls import resolve\nexcept ImportError:\n    from django.core.urlresolvers import resolve\n\ntry:\n    from django.utils.deprecation import MiddlewareMixin\nexcept ImportError:\n    from rollbar.contrib.django.utils import MiddlewareMixin\n\nlog = logging.getLogger(__name__)\n\n\nDEFAULTS = {\n    'web_base': 'https://rollbar.com',\n    'enabled': True,\n    'patch_debugview': True,\n    'exception_level_filters': [\n        (Http404, 'warning')\n    ]\n}\n\n\ndef _patch_debugview(rollbar_web_base):\n    try:\n        from django.views import debug\n    except ImportError:\n        return\n\n    if rollbar_web_base.endswith('/'):\n        rollbar_web_base = rollbar_web_base[:-1]\n\n    # modify the TECHNICAL_500_TEMPLATE\n    new_data = \"\"\"\n{% if view_in_rollbar_url %}\n  <h3 style=\"margin-bottom:15px;\"><a href=\"{{ view_in_rollbar_url }}\" target=\"_blank\">View in Rollbar</a></h3>\n{% endif %}\n    \"\"\"\n\n    insert_before = '<table class=\"meta\">'\n    replacement = new_data + insert_before\n\n    if hasattr(debug, 'TECHNICAL_500_TEMPLATE'):\n        if new_data in debug.TECHNICAL_500_TEMPLATE:\n            return\n        debug.TECHNICAL_500_TEMPLATE = debug.TECHNICAL_500_TEMPLATE.replace(insert_before, replacement, 1)\n    elif hasattr(debug, 'CURRENT_DIR'):\n        # patch ExceptionReporter.get_traceback_html if this version of Django is using\n        # the file system templates rather than the ones in code\n        # This code comes from:\n        # https://github.com/django/django/blob/d79cf1e9e2887aa12567c8f27e384195253cb847/django/views/debug.py#L329,L334\n        # There are theoretical issues with the code below, for example t.render could throw because\n        # t might be None, but this is the code from Django\n        from pathlib import Path\n        from django.template import Context\n        def new_get_traceback_html(exception_reporter):\n            \"\"\"Return HTML version of debug 500 HTTP error page.\"\"\"\n            with Path(debug.CURRENT_DIR, 'templates', 'technical_500.html').open() as fh:\n                template_string = fh.read()\n                template_string = template_string.replace(insert_before, replacement, 1)\n                t = debug.DEBUG_ENGINE.from_string(template_string)\n            c = Context(exception_reporter.get_traceback_data(), use_l10n=False)\n            return t.render(c)\n        debug.ExceptionReporter.get_traceback_html = new_get_traceback_html\n    else:\n        # patch ExceptionReporter.get_traceback_html for Django versions 4.0+\n        def new_get_traceback_html(self):\n            \"\"\"Return HTML version of debug 500 HTTP error page.\"\"\"\n            with self.html_template_path.open(encoding='utf-8') as fh:\n                t = debug.DEBUG_ENGINE.from_string(fh.read())\n            c = Context(self.get_traceback_data(), use_l10n=False)\n            return t.render(c)\n\n    if hasattr(debug.ExceptionReporter, '__rollbar__patched'):\n        return\n\n    # patch ExceptionReporter.get_traceback_data\n    old_get_traceback_data = debug.ExceptionReporter.get_traceback_data\n    def new_get_traceback_data(exception_reporter):\n        data = old_get_traceback_data(exception_reporter)\n        try:\n            item_uuid = exception_reporter.request.META.get('rollbar.uuid')\n            if item_uuid:\n                url = '%s/item/uuid/?uuid=%s' % (rollbar_web_base, item_uuid)\n                data['view_in_rollbar_url'] = url\n        except:\n            log.exception(\"Exception while adding view-in-rollbar link to technical_500_template.\")\n        return data\n    debug.ExceptionReporter.get_traceback_data = new_get_traceback_data\n    debug.ExceptionReporter.__rollbar__patched = True\n\n\n\ndef _should_ignore_404(url):\n    url_patterns = getattr(settings, 'ROLLBAR', {}).get('ignorable_404_urls', ())\n    return any(p.search(url) for p in url_patterns)\n\ndef _apply_sensitive_post_params(request):\n    sensitive_post_parameters = getattr(\n        request, \"sensitive_post_parameters\", []\n    )\n    if not sensitive_post_parameters:\n        return\n    mutable = request.POST._mutable\n    request.POST._mutable = True\n\n    if sensitive_post_parameters == \"__ALL__\":\n        for param in request.POST:\n            request.POST[param] = \"******\"\n        return\n\n    for param in sensitive_post_parameters:\n        if param in request.POST:\n            request.POST[param] = \"******\"\n    request.POST._mutable = mutable\n\nclass RollbarNotifierMiddleware(MiddlewareMixin):\n    def __init__(self, get_response=None):\n        super(RollbarNotifierMiddleware, self).__init__(get_response)\n\n        self.settings = getattr(settings, 'ROLLBAR', {})\n        if not self.settings.get('access_token'):\n            raise MiddlewareNotUsed\n\n        if not self._get_setting('enabled'):\n            raise MiddlewareNotUsed\n\n        self._ensure_log_handler()\n\n        kw = self.settings.copy()\n        access_token = kw.pop('access_token')\n        environment = kw.pop('environment', 'development' if settings.DEBUG else 'production')\n        kw.setdefault('exception_level_filters', DEFAULTS['exception_level_filters'])\n\n        # ignorable_404_urls is only relevant for this middleware not as an argument to init\n        kw.pop('ignorable_404_urls', None)\n\n        rollbar.init(access_token, environment, **kw)\n\n        def hook(request, data):\n            try:\n                # try django 1.5 method for getting url_name\n                url_name = request.resolver_match.url_name\n            except:\n                # fallback to older method\n                try:\n                    url_name = resolve(request.path_info).url_name\n                except:\n                    url_name = None\n\n            if url_name:\n                data['context'] = url_name\n\n            data['framework'] = 'django'\n\n            if request and hasattr(request, 'META'):\n                request.META['rollbar.uuid'] = data['uuid']\n\n        rollbar.BASE_DATA_HOOK = hook\n\n        # monkeypatch debug module\n        if self._get_setting('patch_debugview'):\n            try:\n                _patch_debugview(self._get_setting('web_base'))\n            except Exception as e:\n                log.error(\n                    \"Rollbar - unable to monkeypatch debugview to add 'View in Rollbar' link.\"\n                    \" To disable, set `ROLLBAR['patch_debugview'] = False` in settings.py.\"\n                    \" Exception was: %r\", e\n                )\n\n    def __call__(self, request):\n        headers = {}\n        for k, v in request.META.items():\n            if k.startswith('HTTP_'):\n                header_name = '-'.join(k[len('HTTP_'):].replace('_', ' ').title().split(' '))\n                headers[header_name] = v\n        set_current_session(headers)\n\n        try:\n            response = self.get_response(request)\n            return response\n        finally:\n            reset_current_session()\n\n    def _ensure_log_handler(self):\n        \"\"\"\n        If there's no log configuration, set up a default handler.\n        \"\"\"\n        if log.handlers:\n            return\n        handler = logging.StreamHandler()\n        formatter = logging.Formatter(\n            '%(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s')\n        handler.setFormatter(formatter)\n        log.addHandler(handler)\n\n    def _get_setting(self, name, default=None):\n        try:\n            return self.settings[name]\n        except KeyError:\n            if name in DEFAULTS:\n                default_val = DEFAULTS[name]\n                if hasattr(default_val, '__call__'):\n                    return default_val()\n                return default_val\n            return default\n\n    def get_extra_data(self, request, exc):\n        return\n\n    def get_payload_data(self, request, exc):\n        return\n\n    def process_response(self, request, response):\n        return response\n\n    def process_exception(self, request, exc):\n        if isinstance(exc, Http404) and _should_ignore_404(request.get_full_path()):\n            return\n        _apply_sensitive_post_params(request)\n\n        rollbar.report_exc_info(\n            sys.exc_info(),\n            request,\n            extra_data=self.get_extra_data(request, exc),\n            payload_data=self.get_payload_data(request, exc),\n        )\n\n\nclass RollbarNotifierMiddlewareOnly404(MiddlewareMixin):\n    def get_extra_data(self, request, exc):\n        return\n\n    def get_payload_data(self, request, exc):\n        return\n\n    def process_response(self, request, response):\n        if response.status_code != 404:\n            return response\n\n        if _should_ignore_404(request.get_full_path()):\n            return response\n\n        try:\n            if hasattr(request, '_rollbar_notifier_original_http404_exc_info'):\n                exc_type, exc_value, exc_traceback = request._rollbar_notifier_original_http404_exc_info\n                if exc_value is None:\n                    exc_value = Http404()\n                raise exc_value.with_traceback(exc_traceback)\n            else:\n                raise Http404()\n        except Exception as exc:\n            _apply_sensitive_post_params(request)\n            rollbar.report_exc_info(\n                sys.exc_info(),\n                request,\n                extra_data=self.get_extra_data(request, exc),\n                payload_data=self.get_payload_data(request, exc),\n            )\n        return response\n\n\nclass RollbarNotifierMiddlewareExcluding404(RollbarNotifierMiddleware):\n    def process_exception(self, request, exc):\n        if isinstance(exc, Http404):\n            request._rollbar_notifier_original_http404_exc_info = sys.exc_info()\n        else:\n            super(RollbarNotifierMiddlewareExcluding404, self).process_exception(request, exc)\n"
  },
  {
    "path": "rollbar/contrib/django/models.py",
    "content": "\"\"\"\nNo models - this file is here so django recognizes this as an application for running unit tests.\n\"\"\"\n"
  },
  {
    "path": "rollbar/contrib/django/tests.py",
    "content": "\"\"\"\nUnit tests\n\"\"\"\nfrom django.test import TestCase\nfrom django.conf import settings\n\nclass BasicTests(TestCase):\n    def test_configuration(self):\n        \"\"\"\n        Test that the configuration is sane.\n        \"\"\"\n        self.assertTrue('ROLLBAR' in dir(settings),\n            msg='The ROLLBAR setting is not present.')\n        self.assertTrue(settings.ROLLBAR.get('access_token'),\n            msg='The ROLLBAR[\"access_token\"] setting is blank.')\n        \n"
  },
  {
    "path": "rollbar/contrib/django/utils.py",
    "content": "class MiddlewareMixin(object):\n    def __init__(self, get_response=None):\n        super(MiddlewareMixin, self).__init__()\n"
  },
  {
    "path": "rollbar/contrib/django_rest_framework/__init__.py",
    "content": "try:\n    from django.core.exceptions import ImproperlyConfigured\nexcept ImportError:\n    ImproperlyConfigured = RuntimeError\n\ntry:\n    from rest_framework.views import exception_handler as _exception_handler\nexcept (ImportError, ImproperlyConfigured):\n    _exception_handler = None\n\n\ndef post_exception_handler(exc, context):\n    # This is to be used with the Django REST Framework (DRF) as its\n    # global exception handler.  It replaces the POST data of the Django\n    # request with the parsed data from the DRF.  This is necessary\n    # because we cannot read the request data/stream more than once.\n    # This will allow us to see the parsed POST params in the rollbar\n    # exception log.\n\n    if _exception_handler is None:\n        raise ImproperlyConfigured(\n            'Could not import rest_framework.views.exception_handler')\n\n    try:\n        context['request']._request.POST = context['request'].data\n    except Exception:\n        pass\n\n    return _exception_handler(exc, context)\n"
  },
  {
    "path": "rollbar/contrib/fastapi/__init__.py",
    "content": "__all__ = ['add_to', 'ReporterMiddleware', 'LoggerMiddleware', 'get_current_request']\n\n# Optional requirements:\n#\n# - FastAPI requires `python-multipart` package to support requests body parsing\n# - `LoggerMiddleware` and `get_current_request` require `aiocontextvars` package\n#   to be installed when running in Python 3.6\n\nfrom .middleware import ReporterMiddleware\nfrom .logger import LoggerMiddleware\nfrom .routing import add_to\n\n# Do not modify the returned request object\nfrom rollbar.contrib.starlette import get_current_request\n"
  },
  {
    "path": "rollbar/contrib/fastapi/logger.py",
    "content": "__all__ = ['LoggerMiddleware']\n\nfrom fastapi import __version__\n\nfrom rollbar.contrib.asgi.integration import integrate\nfrom rollbar.contrib.starlette import LoggerMiddleware as StarletteLoggerMiddleware\n\n\n@integrate(framework_name=f'fastapi {__version__}')\nclass LoggerMiddleware(StarletteLoggerMiddleware):\n    ...\n"
  },
  {
    "path": "rollbar/contrib/fastapi/middleware.py",
    "content": "__all__ = ['ReporterMiddleware']\n\nfrom fastapi import __version__\n\nfrom rollbar.contrib.asgi.integration import integrate\nfrom rollbar.contrib.starlette import ReporterMiddleware as StarletteReporterMiddleware\n\n\n@integrate(framework_name=f'fastapi {__version__}')\nclass ReporterMiddleware(StarletteReporterMiddleware):\n    ...\n"
  },
  {
    "path": "rollbar/contrib/fastapi/routing.py",
    "content": "__all__ = ['add_to']\n\nimport logging\nimport sys\nfrom typing import Callable, Optional, Type, Union\n\nfrom fastapi import APIRouter, FastAPI, __version__\nfrom fastapi.routing import APIRoute\n\nfrom rollbar.lib.session import reset_current_session, set_current_session\n\ntry:\n    from fastapi import Request, Response\nexcept ImportError:\n    # Added in FastAPI v0.51.0\n    from starlette.requests import Request\n    from starlette.responses import Response\n\nimport rollbar\nfrom .utils import fastapi_min_version, get_installed_middlewares, has_bare_routing\nfrom rollbar.contrib.asgi.integration import integrate\nfrom rollbar.contrib.starlette.requests import store_current_request\nfrom rollbar.lib._async import RollbarAsyncError, try_report\n\nlog = logging.getLogger(__name__)\n\n\n@fastapi_min_version('0.41.0')\n@integrate(framework_name=f'fastapi {__version__}')\ndef add_to(app_or_router: Union[FastAPI, APIRouter]) -> Optional[Type[APIRoute]]:\n    \"\"\"\n    Adds RollbarLoggingRoute handler to the router app.\n\n    This is the recommended way for integration with FastAPI.\n    Alternatively to using middleware, the handler may fill\n    more data in the payload (e.g. request body).\n\n    app_or_router: FastAPI app or router\n\n    Note: The route handler must be added before adding user routes\n\n    Requirements: FastAPI v0.41.0+\n\n    Example usage:\n\n    from fastapi import FastAPI\n    from rollbar.contrib.fastapi import add_to as rollbar_add_to\n\n    app = FastAPI()\n    rollbar_add_to(app)\n\n    \"\"\"\n\n    if not has_bare_routing(app_or_router):\n        log.error(\n            'RollbarLoggingRoute must to be added to a bare router'\n            ' (before adding routes). See docs for more details.'\n        )\n        return None\n\n    installed_middlewares = get_installed_middlewares(app_or_router)\n    if installed_middlewares:\n        log.warning(\n            f'Detected middleware installed {installed_middlewares}'\n            ' while loading Rollbar route handler.'\n            ' This can cause in duplicate occurrences.'\n        )\n\n    if isinstance(app_or_router, FastAPI):\n        _add_to_app(app_or_router)\n    elif isinstance(app_or_router, APIRouter):\n        _add_to_router(app_or_router)\n    else:\n        log.error('Error adding RollbarLoggingRoute to application.')\n        return None\n\n    return RollbarLoggingRoute\n\n\nclass RollbarLoggingRoute(APIRoute):\n    def get_route_handler(self) -> Callable:\n        router_handler = super().get_route_handler()\n\n        async def rollbar_route_handler(request: Request) -> Response:\n            set_current_session(dict(request.headers))\n            try:\n                store_current_request(request)\n                return await router_handler(request)\n            except Exception:\n                # FastAPI requires the `python-multipart` package to parse the content\n                if not request._stream_consumed:\n                    await request.body()\n                await request.form()\n\n                exc_info = sys.exc_info()\n\n                try:\n                    await try_report(exc_info, request)\n                except RollbarAsyncError:\n                    log.warning(\n                        'Failed to report asynchronously. Trying to report synchronously.'\n                    )\n                    rollbar.report_exc_info(exc_info, request)\n                raise\n            finally:\n                reset_current_session()\n\n        return rollbar_route_handler\n\n\ndef _add_to_app(app):\n    app.router.route_class = RollbarLoggingRoute\n\n\ndef _add_to_router(router):\n    router.route_class = RollbarLoggingRoute\n"
  },
  {
    "path": "rollbar/contrib/fastapi/utils.py",
    "content": "import functools\nimport logging\nfrom typing import Union\n\nimport fastapi\nfrom fastapi import APIRouter, FastAPI\n\nfrom . import ReporterMiddleware as FastAPIReporterMiddleware\nfrom rollbar.contrib.starlette import ReporterMiddleware as StarletteReporterMiddleware\nfrom rollbar.contrib.asgi import ReporterMiddleware as ASGIReporterMiddleware\n\nlog = logging.getLogger(__name__)\n\n\nclass FastAPIVersionError(Exception):\n    def __init__(self, version, reason=''):\n        err_msg = f'FastAPI {version}+ is required'\n        if reason:\n            err_msg += f' {reason}'\n\n        log.error(err_msg)\n        return super().__init__(err_msg)\n\n\ndef is_current_version_higher_or_equal(current_version, min_version):\n    \"\"\"\n    Compare two version strings and return True if the current version is higher or equal to the minimum version.\n\n    Note: This function only compares the release segment of the version string.\n    \"\"\"\n    def parse_version(version):\n        \"\"\"Parse the release segment of a version string into a list of strings.\"\"\"\n        parsed = ['']\n        current_segment = 0\n        for c in version:\n            if c.isdigit():\n                parsed[current_segment] += c\n            elif c == '.':\n                current_segment += 1\n                parsed.append('')\n            else:\n                break\n        if parsed[-1] == '':\n            parsed.pop()\n        return parsed\n\n    current = tuple(map(int, parse_version(current_version)))\n    minimum = tuple(map(int, parse_version(min_version)))\n    return current >= minimum\n\n\nclass fastapi_min_version:\n    def __init__(self, min_version):\n        self.min_version = min_version\n\n    def __call__(self, func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            if not is_current_version_higher_or_equal(\n                fastapi.__version__,\n                self.min_version,\n            ):\n                raise FastAPIVersionError(\n                    self.min_version, reason=f'to use {func.__name__}() function'\n                )\n\n            return func(*args, **kwargs)\n\n        return wrapper\n\n\ndef get_installed_middlewares(app):\n    candidates = (\n        FastAPIReporterMiddleware,\n        StarletteReporterMiddleware,\n        ASGIReporterMiddleware,\n    )\n\n    middlewares = []\n\n    if hasattr(app, 'user_middleware'):  # FastAPI v0.51.0+\n        middlewares = [\n            middleware.cls\n            for middleware in app.user_middleware\n            if middleware.cls in candidates\n        ]\n    elif hasattr(app, 'error_middleware'):\n        middleware = app.error_middleware\n\n        while hasattr(middleware, 'app'):\n            if isinstance(middleware, candidates):\n                middlewares.append(middleware)\n            middleware = middleware.app\n\n        middlewares = [middleware.__class__ for middleware in middlewares]\n\n    return middlewares\n\n\ndef has_bare_routing(app_or_router: Union[FastAPI, APIRouter]):\n    if not isinstance(app_or_router, (FastAPI, APIRouter)):\n        return False\n\n    urls = [\n        getattr(app_or_router, 'openapi_url', None),\n        getattr(app_or_router, 'docs_url', None),\n        getattr(app_or_router, 'redoc_url', None),\n        getattr(app_or_router, 'swagger_ui_oauth2_redirect_url', None),\n    ]\n\n    for route in app_or_router.routes:\n        if route is None or route.path in urls:\n            continue\n        return False\n\n    return True\n"
  },
  {
    "path": "rollbar/contrib/flask/__init__.py",
    "content": "\"\"\"\nIntegration with Flask\n\"\"\"\n\nfrom flask import request, got_request_exception, Flask\nimport rollbar\nfrom rollbar.lib.session import reset_current_session, set_current_session\n\n\ndef report_exception(app, exception):\n    rollbar.report_exc_info(request=request)\n\n\ndef _hook(request, data):\n    data['framework'] = 'flask'\n\n    if request:\n        data['context'] = str(request.url_rule)\n\nrollbar.BASE_DATA_HOOK = _hook\n\n\ndef init(app: Flask, access_token, environment='production', scrub_fields=None, url_fields=None, **kw):\n    \"\"\"\n    Initializes the Rollbar Flask integration.\n    \"\"\"\n    with app.app_context():\n        rollbar.init(\n            access_token=access_token,\n            environment=environment,\n            scrub_fields=scrub_fields,\n            url_fields=url_fields,\n            **kw,\n        )\n        # send exceptions from `app` to rollbar, using flask's signal system.\n        got_request_exception.connect(report_exception, app)\n\n    @app.before_request\n    def before_request():\n        set_current_session(dict(request.headers))\n\n    @app.teardown_request\n    def teardown_request(exception):\n        reset_current_session()"
  },
  {
    "path": "rollbar/contrib/pyramid/__init__.py",
    "content": "\"\"\"\nPlugin for Pyramid apps to submit errors to Rollbar\n\"\"\"\n\nimport logging\nimport sys\n\nfrom pyramid.httpexceptions import WSGIHTTPException\nfrom pyramid.tweens import EXCVIEW\nfrom pyramid.util import DottedNameResolver\nfrom pyramid.settings import asbool\n\nimport rollbar\nfrom rollbar import set_current_session\nfrom rollbar.lib.session import reset_current_session\n\nDEFAULT_WEB_BASE = 'https://rollbar.com'\nBOOLEAN_SETTINGS = [\n    'rollbar.enabled', 'rollbar.allow_logging_basic_config',\n    'rollbar.verify_https'\n]\n\nlog = logging.getLogger(__name__)\n\n\nEXCEPTION_BLOCKLIST = (WSGIHTTPException,)\nEXCEPTION_SAFELIST = tuple()\n\n\ndef handle_error(request, exception, exc_info):\n    if(\n            isinstance(exception, EXCEPTION_BLOCKLIST) and\n            not isinstance(exception, EXCEPTION_SAFELIST)\n    ):\n        return\n    rollbar.report_exc_info(exc_info, request)\n\n\ndef parse_settings(settings):\n    prefix = 'rollbar.'\n    out = {}\n    for k, v in settings.items():\n        if k.startswith(prefix):\n            if k in BOOLEAN_SETTINGS:\n                v = asbool(v)\n            out[k[len(prefix):]] = v\n\n    return out\n\n\ndef rollbar_tween_factory(pyramid_handler, registry):\n    settings = parse_settings(registry.settings)\n\n    def rollbar_tween(request):\n        set_current_session(dict(request.headers))\n        # for testing out the integration\n        try:\n            if (settings.get('allow_test', 'true') == 'true' and\n                    request.GET.get('pyramid_rollbar_test') == 'true'):\n                try:\n                    raise Exception(\"pyramid_rollbar test exception\")\n                except Exception as exc:\n                    handle_error(request, exc, sys.exc_info())\n        except:\n            log.exception(\"Error in pyramid_rollbar_test block\")\n\n        try:\n            response = pyramid_handler(request)\n        except Exception as exc:\n            handle_error(request, exc, sys.exc_info())\n            raise\n        finally:\n            reset_current_session()\n        if request.exception is not None:\n            handle_error(request, request.exception, request.exc_info)\n        return response\n\n    return rollbar_tween\n\n\ndef patch_debugtoolbar(settings):\n    \"\"\"\n    Patches the pyramid_debugtoolbar (if installed) to display a link to the related rollbar item.\n    \"\"\"\n    try:\n        from pyramid_debugtoolbar import tbtools\n    except ImportError:\n        return\n\n    rollbar_web_base = settings.get('rollbar.web_base', DEFAULT_WEB_BASE)\n    if rollbar_web_base.endswith('/'):\n        rollbar_web_base = rollbar_web_base[:-1]\n\n    def insert_rollbar_console(request, html):\n        # insert after the closing </h1>\n        item_uuid = request.environ.get('rollbar.uuid')\n        if not item_uuid:\n            return html\n\n        url = '%s/item/uuid/?uuid=%s' % (rollbar_web_base, item_uuid)\n        link = '<a style=\"color:white;\" href=\"%s\">View in Rollbar</a>' % url\n        new_data = \"<h2>Rollbar: %s</h2>\" % link\n        insertion_marker = \"</h1>\"\n        replacement = insertion_marker + new_data\n        return html.replace(insertion_marker, replacement, 1)\n\n    # patch tbtools.Traceback.render_full\n    old_render_full = tbtools.Traceback.render_full\n\n    def new_render_full(self, request, *args, **kw):\n        html = old_render_full(self, request, *args, **kw)\n        return insert_rollbar_console(request, html)\n\n    tbtools.Traceback.render_full = new_render_full\n\n\ndef includeme(config):\n    \"\"\"\n    Pyramid entry point\n    \"\"\"\n    settings = config.registry.settings\n\n    config.add_tween('rollbar.contrib.pyramid.rollbar_tween_factory', over=EXCVIEW)\n\n    # run patch_debugtoolbar, unless they disabled it\n    if asbool(settings.get('rollbar.patch_debugtoolbar', True)):\n        patch_debugtoolbar(settings)\n\n    def hook(request, data):\n        data['framework'] = 'pyramid'\n\n        if request:\n            request.environ['rollbar.uuid'] = data['uuid']\n\n            if request.matched_route:\n                data['context'] = request.matched_route.name\n\n    rollbar.BASE_DATA_HOOK = hook\n\n    kw = parse_settings(settings)\n\n    access_token = kw.pop('access_token')\n    environment = kw.pop('environment', 'production')\n\n    if kw.get('scrub_fields'):\n        kw['scrub_fields'] = {str.strip(x) for x in kw.get('scrub_fields').split('\\n') if x}\n\n    if kw.get('exception_level_filters'):\n        r = DottedNameResolver()\n        exception_level_filters = []\n        for line in kw.get('exception_level_filters').split('\\n'):\n            if line:\n                dotted_path, level = line.split()\n\n                try:\n                    cls = r.resolve(dotted_path)\n                    exception_level_filters.append((cls, level))\n                except ImportError:\n                    log.error('Could not import %r' % dotted_path)\n\n        kw['exception_level_filters'] = exception_level_filters\n\n    kw['enabled'] = asbool(kw.get('enabled', True))\n\n    rollbar.init(access_token, environment, **kw)\n\n\ndef create_rollbar_middleware(app, global_config=None, **kw):\n    access_token = kw.pop('access_token')\n    environment = kw.pop('environment', 'production')\n\n    rollbar.init(access_token, environment, **kw)\n    return RollbarMiddleware(global_config or {}, app)\n\n\nclass RollbarMiddleware(object):\n    def __init__(self, settings, app):\n        self.settings = settings\n        self.app = app\n\n    def __call__(self, environ, start_resp):\n        try:\n            return self.app(environ, start_resp)\n        except Exception as exc:\n            from pyramid.request import Request\n            handle_error(Request(environ), exc, sys.exc_info())\n            raise\n"
  },
  {
    "path": "rollbar/contrib/quart/__init__.py",
    "content": "\"\"\"\nIntegration with Quart\n\"\"\"\n\nfrom quart import request\nimport rollbar\n\n\ndef report_exception(app, exception):\n    rollbar.report_exc_info(request=request)\n\n\ndef _hook(request, data):\n    data['framework'] = 'quart'\n\n    if request:\n        data['context'] = str(request.url_rule)\n\nrollbar.BASE_DATA_HOOK = _hook\n"
  },
  {
    "path": "rollbar/contrib/rq/__init__.py",
    "content": "\"\"\"\nException handler hook for RQ (http://python-rq.org/)\n\nHow to use: \n\n1. Instead of using the default \"rqworker\" script to run the worker, write your own short script\nas shown in this example: \nhttps://github.com/nvie/rq/blob/master/examples/run_worker.py\n\n2. In this script, initialize rollbar with `handler='blocking'`, for example:\n\nrollbar.init('your access token', 'production', handler='blocking')\n\n3. After constructing the worker but before calling `.work()`, add\n`rollbar.contrib.rq.exception_handler` as an exception handler.\n\nFull example:\n\n```\nimport rollbar\nfrom rq import Connection, Queue, Worker\n\nif __name__ == '__main__':\n    rollbar.init('your_access_token', 'production', handler='blocking')\n    with Connection():\n        q = Queue()\n        worker = Worker(q)\n        worker.push_exc_handler(rollbar.contrib.rq.exception_handler)\n        worker.work()\n```\n\"\"\"\n\nimport rollbar\n\n\ndef exception_handler(job, *exc_info):\n    \"\"\"\n    Called by RQ when there is a failure in a worker.\n\n    NOTE: Make sure that in your RQ worker process, rollbar.init() has been called with\n    handler='blocking'. The default handler, 'thread', does not work from inside an RQ worker.\n    \"\"\"\n    # Report data about the job with the exception.\n    job_info = job.to_dict()\n    # job_info['data'] is the pickled representation of the job, and doesn't json-serialize well.\n    # repr() works nicely.\n    job_info['data'] = repr(job_info['data'])\n\n    extra_data = {'job': job_info}\n    payload_data = {'framework': 'rq'}\n    \n    rollbar.report_exc_info(exc_info, extra_data=extra_data, payload_data=payload_data)\n\n    # continue to the next handler\n    return True\n"
  },
  {
    "path": "rollbar/contrib/starlette/__init__.py",
    "content": "__all__ = ['ReporterMiddleware', 'LoggerMiddleware', 'get_current_request']\n\n# Optional requirements:\n#\n# - Starlette requires `python-multipart` package to support requests body parsing\n# - `LoggerMiddleware` and `get_current_request` require `aiocontextvars` package\n#   to be installed when running in Python 3.6\n\nfrom .middleware import ReporterMiddleware\nfrom .logger import LoggerMiddleware\n\n# Do not modify the returned request object\nfrom .requests import get_current_request\n"
  },
  {
    "path": "rollbar/contrib/starlette/logger.py",
    "content": "__all__ = ['LoggerMiddleware']\n\nimport logging\nimport sys\n\nfrom starlette import __version__\nfrom starlette.types import ASGIApp, Receive, Scope, Send\n\nfrom rollbar.contrib.asgi import ReporterMiddleware as ASGIReporterMiddleware\nfrom rollbar.contrib.asgi.integration import integrate\nfrom rollbar.contrib.starlette.requests import store_current_request\n\nlog = logging.getLogger(__name__)\n\n\n@integrate(framework_name=f'starlette {__version__}')\nclass LoggerMiddleware(ASGIReporterMiddleware):\n    def __init__(self, app: ASGIApp) -> None:\n        if sys.version_info < (3, 6):\n            log.error(\n                'LoggerMiddleware requires Python 3.7+ (or 3.6 with `aiocontextvars` package)'\n            )\n            raise RuntimeError(\n                'LoggerMiddleware requires Python 3.7+ (or 3.6 with `aiocontextvars` package)'\n            )\n\n        super().__init__(app)\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        store_current_request(scope, receive)\n\n        await self.app(scope, receive, send)\n"
  },
  {
    "path": "rollbar/contrib/starlette/middleware.py",
    "content": "import logging\nimport sys\n\nfrom starlette import __version__\nfrom starlette.requests import Request\nfrom starlette.types import Receive, Scope, Send\n\nimport rollbar\nfrom .requests import store_current_request\nfrom rollbar.contrib.asgi import ReporterMiddleware as ASGIReporterMiddleware\nfrom rollbar.contrib.asgi.integration import integrate\nfrom rollbar.lib._async import RollbarAsyncError, try_report\nfrom rollbar.lib.session import set_current_session, reset_current_session\n\nlog = logging.getLogger(__name__)\n\n\n@integrate(framework_name=f'starlette {__version__}')\nclass ReporterMiddleware(ASGIReporterMiddleware):\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        if scope['type'] == 'http':\n            set_current_session(self._format_headers(scope['headers']))\n        try:\n            store_current_request(scope, receive)\n            await self.app(scope, receive, send)\n        except Exception:\n            if scope['type'] == 'http':\n                request = Request(scope, receive)\n\n                # Consuming the request body in Starlette middleware is problematic.\n                # See: https://github.com/encode/starlette/issues/495#issuecomment-494008175\n                #\n                # Uncomment lines below if you know the risks.\n                #\n                # Starlette requires the `python-multipart` package to parse the content\n                # await request.body()\n                # await request.form()\n\n                exc_info = sys.exc_info()\n\n                try:\n                    await try_report(exc_info, request)\n                except RollbarAsyncError:\n                    log.warning(\n                        'Failed to report asynchronously. Trying to report synchronously.'\n                    )\n                    rollbar.report_exc_info(exc_info, request)\n            raise\n        finally:\n            if scope['type'] == 'http':\n                reset_current_session()\n"
  },
  {
    "path": "rollbar/contrib/starlette/requests.py",
    "content": "__all__ = ['get_current_request']\n\nimport logging\nimport sys\nfrom typing import Optional, Union\n\nfrom starlette.requests import Request\nfrom starlette.types import Receive, Scope\n\nlog = logging.getLogger(__name__)\n\nif sys.version_info[:2] == (3, 6):\n    # Backport PEP 567\n    try:\n        import aiocontextvars\n    except ImportError:\n        # Do not raise an exception as the module is exported to package API\n        # but is still optional\n        log.error(\n            'Python 3.6 requires `aiocontextvars` package to be installed'\n            ' to support global access to request objects'\n        )\n\ntry:\n    from contextvars import ContextVar\nexcept ImportError:\n    ContextVar = None\n\nif ContextVar:\n    _current_request: ContextVar[Optional[Request]] = ContextVar(\n        'rollbar-request-object', default=None\n    )\n\n\ndef get_current_request() -> Optional[Request]:\n    \"\"\"\n    Return current request.\n\n    Do NOT modify the returned request object.\n    \"\"\"\n\n    if ContextVar is None:\n        log.error(\n            'Python 3.7+ (or aiocontextvars package)'\n            ' is required to receive current request.'\n        )\n        return None\n\n    return _current_request.get()\n\n\ndef store_current_request(\n    request_or_scope: Union[Request, Scope], receive: Optional[Receive] = None\n) -> Optional[Request]:\n    if ContextVar is None:\n        return None\n\n    if receive is None:\n        request = request_or_scope\n    elif request_or_scope['type'] == 'http':\n        request = Request(request_or_scope, receive)\n    else:\n        request = None\n\n    _current_request.set(request)\n    return request\n\n\ndef hasuser(request: Request) -> bool:\n    try:\n        return hasattr(request, 'user')\n    except AssertionError:\n        return False\n"
  },
  {
    "path": "rollbar/examples/asgi/app.py",
    "content": "#!/usr/bin/env python\n\n# ASGI middleware is ASGI v3 compliant. It can integrate with any\n# ASGIv3-compliant applications.\n# This example demonstrates Rollbar integration with the Starlette app.\n#\n# NOTE: Rollbar provides dedicated framework integrations that are more\n# feature-rich. Currently available: Starlette and FastAPI.\n#\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# Optional asynchronous reporting requires HTTPX package to be installed.\n#\n# Run: python app.py\n\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.asgi import ReporterMiddleware as RollbarMiddleware\nfrom starlette.applications import Starlette\nfrom starlette.responses import PlainTextResponse\n\n# Initialize Rollbar SDK with your server-side ACCESS_TOKEN\nrollbar.init(\n    'ACCESS_TOKEN',\n    environment='staging',\n    handler='async',  # For asynchronous reporting use: default, async or httpx\n)\n\n# Integrate Rollbar with Starlette application\napp = Starlette()\napp.add_middleware(RollbarMiddleware)  # should be added as the first middleware\n\n\n# Verify application runs correctly\n#\n# $ curl http://localhost:8888\n@app.route('/')\nasync def root(request):\n    return PlainTextResponse('hello world')\n\n\n# Cause an uncaught exception to be sent to Rollbar\n# GET query params will be sent to Rollbar and available in the UI\n#\n# $ curl http://localhost:8888/error?param1=hello&param2=world\nasync def localfunc(arg1, arg2, arg3):\n    # Both local variables and function arguments will be sent to Rollbar\n    # and available in the UI\n    localvar = 'local variable'\n    cause_error_with_local_variables\n\n\n@app.route('/error')\nasync def error(request):\n    await localfunc('func_arg1', 'func_arg2', 1)\n    return PlainTextResponse(\"You shouldn't be seeing this\")\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/django/app.py",
    "content": "import os\nimport sys\n\nimport django\nfrom django.conf import settings\n\n\n# use settings compatible with the installed Django version\n# v1.10+ requires MIDDLEWARE keyname\n# older versions require MIDDLEWARE_CLASSES keyname\n\nROLLBAR_CONFIG = {\n    'access_token': 'POST_SERVER_ITEM_ACCESS_TOKEN',\n    'environment': 'development',\n    'branch': 'master',\n    'root': os.getcwd()\n}\n\nMIDDLEWARE_CONFIG = (\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n\n    # Rollbar middleware\n    'rollbar.contrib.django.middleware.RollbarNotifierMiddleware',\n)\n\nif django.VERSION >= (1, 10):\n    settings.configure(\n        DEBUG=True,\n        SECRET_KEY='thisisthesecretkey',\n        ROOT_URLCONF=__name__,\n        ROLLBAR = ROLLBAR_CONFIG,\n        MIDDLEWARE = MIDDLEWARE_CONFIG,\n    )\nelse:\n    settings.configure(\n        DEBUG=True,\n        SECRET_KEY='thisisthesecretkey',\n        ROOT_URLCONF=__name__,\n        ROLLBAR = ROLLBAR_CONFIG,\n        MIDDLEWARE_CLASSES = MIDDLEWARE_CONFIG,\n    )\n\nfrom django.conf.urls import url\nfrom django.http import HttpResponse\n\n\ndef index(request):\n    return HttpResponse('Hello World')\n\ndef error(request):\n    foo()\n    return HttpResponse('You shouldn\\'t be seeing this')\n\n\nurlpatterns = (\n    url(r'^$', index),\n    url(r'^error$', error),\n)\n\nif __name__ == \"__main__\":\n    from django.core.management import execute_from_command_line\n\n    execute_from_command_line(sys.argv)\n"
  },
  {
    "path": "rollbar/examples/fastapi/app.py",
    "content": "#!/usr/bin/env python\n\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# Optional asynchronous reporting requires HTTPX package to be installed.\n#\n# NOTE: This example requires FastAPI v0.41.0+ (see app_middleware.py for alternative).\n#\n# Run: python app.py\n\nimport fastapi\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.fastapi import add_to as rollbar_add_to\n\n# Initialize Rollbar SDK with your server-side ACCESS_TOKEN\nrollbar.init(\n    'ACCESS_TOKEN',\n    environment='staging',\n    handler='async',  # For asynchronous reporting use: default, async or httpx\n    include_request_body=True,\n)\n\n# Integrate Rollbar with FastAPI application before adding routes to the app\napp = fastapi.FastAPI()\nrollbar_add_to(app)\n\n\n# Verify application runs correctly\n#\n# $ curl http://localhost:8888\n@app.get('/')\nasync def read_root():\n    return {'hello': 'world'}\n\n\n# Cause an uncaught exception to be sent to Rollbar\n# GET query params will be sent to Rollbar and available in the UI\n#\n# $ curl http://localhost:8888/error?param1=hello&param2=world\nasync def localfunc(arg1, arg2, arg3):\n    # Both local variables and function arguments will be sent to Rollbar\n    # and available in the UI\n    localvar = 'local variable'\n    cause_error_with_local_variables\n\n\n@app.get('/error')\nasync def read_error():\n    await localfunc('func_arg1', 'func_arg2', 1)\n    return {'result': \"You shouldn't be seeing this\"}\n\n\n# Cause an uncaught exception to be sent to Rollbar\n# POST request body will be sent to Rollbar and available in the UI\n#\n# curl http://localhost:8888/body -d '{\"param1\": \"hello\", \"param2\": \"world\"}'\n@app.post('/body')\nasync def read_body():\n    cause_error_with_body\n    return {'result': \"You shouldn't be seeing this\"}\n\n\n# Cause an uncaught exception to be sent to Rollbar\n# POST form data will be sent to Rollbar and available in the UI\n#\n# curl http://localhost:8888/form -F 'param1=hello' -F 'param2=world'\n@app.post('/form')\nasync def read_form():\n    cause_error_with_form\n    return {'result': \"You shouldn't be seeing this\"}\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/fastapi/app_global_request.py",
    "content": "#!/usr/bin/env python\n\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# NOTE: Python 3.6 requires aiocontextvars package to be installed.\n#\n# Run: python app_global_request.py\n\nimport fastapi\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.fastapi import LoggerMiddleware\n\n# Integrate Rollbar with FastAPI application\napp = fastapi.FastAPI()\napp.add_middleware(LoggerMiddleware)  # should be added as the last middleware\n\n\nasync def get_user_agent():\n    # Global access to the current request object\n    request = rollbar.get_request()\n\n    user_agent = request.headers['User-Agent']\n    return user_agent\n\n\n# $ curl -i http://localhost:8888\n@app.get('/')\nasync def read_root():\n    user_agent = await get_user_agent()\n    return {'user-agent': user_agent}\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/fastapi/app_logger.py",
    "content": "#!/usr/bin/env python\n\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# NOTE: Python 3.6 requires aiocontextvars package to be installed.\n#       Optional asynchronous reporting requires HTTPX package to be installed.\n#\n# Run: python app_logger.py\n\nimport logging\n\nimport fastapi\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.fastapi import LoggerMiddleware\nfrom rollbar.logger import RollbarHandler\n\n# Initialize Rollbar SDK with your server-side ACCESS_TOKEN\nrollbar.init(\n    'ACCESS_TOKEN',\n    environment='staging',\n    handler='async',  # For asynchronous reporting use: default, async or httpx\n)\n\n# Set root logger to log DEBUG and above\nlogger = logging.getLogger(__name__)\nlogger.setLevel(logging.DEBUG)\n\n# Report ERROR and above to Rollbar\nrollbar_handler = RollbarHandler()\nrollbar_handler.setLevel(logging.ERROR)\n\n# Attach Rollbar handler to the root logger\nlogger.addHandler(rollbar_handler)\n\n# Integrate Rollbar with FastAPI application\napp = fastapi.FastAPI()\napp.add_middleware(LoggerMiddleware)  # should be added as the last middleware\n\n\n# GET query params will be sent to Rollbar and available in the UI\n# $ curl http://localhost:8888?param1=hello&param2=world\n@app.get('/')\nasync def read_root():\n    # Report log entries\n    logger.critical('Critical message sent to Rollbar')\n    logger.error('Error message sent to Rollbar')\n\n    # Ignore log entries\n    logger.warning('Warning message is not sent to Rollbar')\n    logger.info('Info message is not sent to Rollbar')\n    logger.debug('Debug message is not sent to Rollbar')\n\n    return {'hello': 'world'}\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/fastapi/app_middleware.py",
    "content": "#!/usr/bin/env python\n\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# Optional asynchronous reporting requires HTTPX package to be installed.\n#\n# NOTE: FastAPI middlewares don't allow to collect streamed content like a request body.\n#       You may consider to use routing integration instead (see app.py example).\n#\n# Run: python app_middleware.py\n\nimport fastapi\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.fastapi import ReporterMiddleware as RollbarMiddleware\n\n# Initialize Rollbar SDK with your server-side ACCESS_TOKEN\nrollbar.init(\n    'ACCESS_TOKEN',\n    environment='staging',\n    handler='async',  # For asynchronous reporting use: default, async or httpx\n)\n\n# Integrate Rollbar with FastAPI application\napp = fastapi.FastAPI()\napp.add_middleware(RollbarMiddleware)  # should be added as the first middleware\n\n\n# Verify application runs correctly\n#\n# $ curl http://localhost:8888\n@app.get('/')\nasync def read_root():\n    return {'hello': 'world'}\n\n\n# Cause an uncaught exception to be sent to Rollbar\n# GET query params will be sent to Rollbar and available in the UI\n#\n# $ curl http://localhost:8888/error?param1=hello&param2=world\nasync def localfunc(arg1, arg2, arg3):\n    # Both local variables and function arguments will be sent to Rollbar\n    # and available in the UI\n    localvar = 'local variable'\n    cause_error_with_local_variables\n\n\n@app.get('/error')\nasync def read_error():\n    await localfunc('func_arg1', 'func_arg2', 1)\n    return {'result': \"You shouldn't be seeing this\"}\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/flask/app.py",
    "content": "# NOTE: pyrollbar requires both `Flask` and `blinker` packages to be installed first\nfrom flask import Flask\nfrom flask import got_request_exception\n\nimport rollbar\nimport rollbar.contrib.flask\n\n\napp = Flask(__name__)\n\n\nwith app.app_context():\n    rollbar.init('ACCESS_TOKEN', environment='development')\n    # send exceptions from `app` to rollbar, using flask's signal system.\n    got_request_exception.connect(rollbar.contrib.flask.report_exception, app)\n\n@app.route('/')\ndef root():\n    foo()\n    return '<html><body>Hello World</body></html>'\n\nif __name__ == '__main__':\n    app.run()\n"
  },
  {
    "path": "rollbar/examples/starlette/app.py",
    "content": "#!/usr/bin/env python\n\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# Optional asynchronous reporting requires HTTPX package to be installed.\n#\n# NOTE: Starlette middlewares don't allow to collect streamed content like a request body.\n#\n# Run: python app.py\n\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.starlette import ReporterMiddleware as RollbarMiddleware\nfrom starlette.applications import Starlette\nfrom starlette.responses import PlainTextResponse\n\n# Initialize Rollbar SDK with your server-side ACCESS_TOKEN\nrollbar.init(\n    'ACCESS_TOKEN',\n    environment='staging',\n    handler='async',  # For asynchronous reporting use: default, async or httpx\n)\n\n# Integrate Rollbar with Starlette application\napp = Starlette()\napp.add_middleware(RollbarMiddleware)  # should be added as the first middleware\n\n\n# Verify application runs correctly\n#\n# $ curl http://localhost:8888\n@app.route('/')\nasync def root(request):\n    return PlainTextResponse('hello world')\n\n\n# Cause an uncaught exception to be sent to Rollbar\n# GET query params will be sent to Rollbar and available in the UI\n#\n# $ curl http://localhost:8888/error?param1=hello&param2=world\nasync def localfunc(arg1, arg2, arg3):\n    # Both local variables and function arguments will be sent to Rollbar\n    # and available in the UI\n    localvar = 'local variable'\n    cause_error_with_local_variables\n\n\n@app.route('/error')\nasync def error(request):\n    await localfunc('func_arg1', 'func_arg2', 1)\n    return PlainTextResponse(\"You shouldn't be seeing this\")\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/starlette/app_global_request.py",
    "content": "#!/usr/bin/env python\n\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# NOTE: Python 3.6 requires aiocontextvars package to be installed.\n#\n# Run: python app_global_request.py\n\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.starlette import LoggerMiddleware\nfrom starlette.applications import Starlette\nfrom starlette.responses import JSONResponse\n\n# Integrate Rollbar with Starlette application\napp = Starlette()\napp.add_middleware(LoggerMiddleware)  # should be added as the last middleware\n\n\nasync def get_user_agent():\n    # Global access to the current request object\n    request = rollbar.get_request()\n\n    user_agent = request.headers['User-Agent']\n    return user_agent\n\n\n# $ curl -i http://localhost:8888\n@app.route('/')\nasync def root(request):\n    user_agent = await get_user_agent()\n    return JSONResponse({'user-agent': user_agent})\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/starlette/app_logger.py",
    "content": "#!/usr/bin/env python\n\n# This example uses Uvicorn package that must be installed. However, it can be\n# replaced with any other ASGI-compliant server.\n#\n# NOTE: Python 3.6 requires aiocontextvars package to be installed.\n#       Optional asynchronous reporting requires HTTPX package to be installed.\n#\n# Run: python app_logger.py\n\nimport logging\n\nimport rollbar\nimport uvicorn\n\nfrom rollbar.contrib.starlette import LoggerMiddleware\nfrom rollbar.logger import RollbarHandler\nfrom starlette.applications import Starlette\nfrom starlette.responses import PlainTextResponse\n\n# Initialize Rollbar SDK with your server-side ACCESS_TOKEN\nrollbar.init(\n    'ACCESS_TOKEN',\n    environment='staging',\n    handler='async',  # For asynchronous reporting use: default, async or httpx\n)\n\n# Set root logger to log DEBUG and above\nlogger = logging.getLogger(__name__)\nlogger.setLevel(logging.DEBUG)\n\n# Report ERROR and above to Rollbar\nrollbar_handler = RollbarHandler()\nrollbar_handler.setLevel(logging.ERROR)\n\n# Attach Rollbar handler to the root logger\nlogger.addHandler(rollbar_handler)\n\n# Integrate Rollbar with Starlette application\napp = Starlette()\napp.add_middleware(LoggerMiddleware)  # should be added as the last middleware\n\n\n# GET query params will be sent to Rollbar and available in the UI\n# $ curl http://localhost:8888?param1=hello&param2=world\n@app.route('/')\nasync def root(request):\n    # Report log entries\n    logger.critical('Critical message sent to Rollbar')\n    logger.error('Error message sent to Rollbar')\n\n    # Ignore log entries\n    logger.warning('Warning message is not sent to Rollbar')\n    logger.info('Info message is not sent to Rollbar')\n    logger.debug('Debug message is not sent to Rollbar')\n\n    return PlainTextResponse('hello world')\n\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='localhost', port=8888)\n"
  },
  {
    "path": "rollbar/examples/twisted/simpleserv.py",
    "content": "\n# Copyright (c) Twisted Matrix Laboratories.\n# See LICENSE for details.\n#\n# From https://twistedmatrix.com/documents/current/_downloads/simpleserv.py\n\n# NOTE: pyrollbar requires both `Twisted` and `treq` packages to be installed\n\nfrom twisted.internet import reactor, protocol\n\nimport rollbar\n\n\ndef bar(p):\n    # These local variables will be sent to Rollbar and available in the UI\n    a = 33\n    b = a * 5\n    baz()\n\n\ndef foo():\n    hello = 'world'\n    bar(hello)\n\n\nclass Echo(protocol.Protocol):\n    \"\"\"This is just about the simplest possible protocol\"\"\"\n\n    def dataReceived(self, data):\n        \"As soon as any data is received, write it back.\"\n\n        # Cause an uncaught exception to be sent to Rollbar\n        foo()\n        self.transport.write(data)\n\n\ndef main():\n    rollbar.init('ACCESS_TOKEN', environment='test', handler='twisted')\n\n    \"\"\"This runs the protocol on port 8000\"\"\"\n    factory = protocol.ServerFactory()\n    factory.protocol = Echo\n    reactor.listenTCP(8000, factory)\n    reactor.run()\n\n\n# this only runs if the module was *not* imported\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "rollbar/lib/__init__.py",
    "content": "import base64\nimport collections\nimport copy\nfrom array import array\n\nfrom collections.abc import Mapping\n\nbinary_type = bytes\ninteger_types = int\nnumber_types = (float, int)\nstring_types = str\nsequence_types = (Mapping, list, tuple, set, frozenset, array, collections.deque)\n\n\ndef force_lower(val):\n    try:\n        return val.lower()\n    except:\n        return str(val).lower()\n\n\ndef prefix_match(key, prefixes):\n    if not key:\n        return False\n\n    for prefix in prefixes:\n        if len(prefix) > len(key):\n            continue\n\n        if prefix == key[:len(prefix)]:\n            return True\n\n    return False\n\n\ndef key_in(key, canonicals):\n    if not key:\n        return False\n\n    for c in canonicals:\n        if key_match(key, c):\n            return True\n\n    return False\n\n\ndef key_depth(key, canonicals) -> int:\n    if not key:\n        return 0\n\n    for c in canonicals:\n        if key_match(key, c):\n            return len(c)\n\n    return 0\n\n\ndef key_match(key, canonical):\n    if len(key) < len(canonical):\n        return False\n\n    for k, c in zip(key, canonical):\n        if '*' == c:\n            continue\n        if c == k:\n            continue\n        return False\n\n    return True\n\n\ndef reverse_list_of_lists(l, apply_each_fn=None):\n    apply_each_fn = apply_each_fn or (lambda x: x)\n    return [reversed([apply_each_fn(x) for x in inner]) for inner in l or []]\n\n\ndef build_key_matcher(prefixes_or_suffixes, type='prefix', case_sensitive=False):\n    _prefixes = []\n\n    if type == 'prefix':\n        _iter = iter\n    elif type == 'suffix':\n        _iter = reversed\n    else:\n        raise ValueError('type must be either \"prefix\" or \"suffix\"')\n\n    prefixes_or_suffixes = prefixes_or_suffixes or []\n    for prefix in prefixes_or_suffixes:\n        if case_sensitive:\n            # Copy the list of lists\n            _prefix = list(_iter(prefix))\n        else:\n            # Lowercase all of the elements\n            _prefix = [force_lower(x) for x in _iter(prefix)]\n\n        _prefixes.append(_prefix)\n\n    def matcher(prefix_or_suffix):\n        if case_sensitive:\n            prefix = list(_iter(prefix_or_suffix))\n        else:\n            prefix = [force_lower(x) for x in _iter(prefix_or_suffix)]\n\n        return prefix_match(prefix, _prefixes)\n\n    return matcher\n\n\ndef is_builtin_type(obj):\n    return obj.__class__.__module__ in ('__builtin__', 'builtins')\n\n\n# http://www.xormedia.com/recursively-merge-dictionaries-in-python.html\ndef dict_merge(a, b, silence_errors=False):\n    \"\"\"\n    Recursively merges dict's. not just simple a['key'] = b['key'], if\n    both a and bhave a key who's value is a dict then dict_merge is called\n    on both values and the result stored in the returned dictionary.\n    \"\"\"\n\n    if not isinstance(b, dict):\n        return b\n\n    result = a\n    for k, v in b.items():\n        if k in result and isinstance(result[k], dict):\n            result[k] = dict_merge(result[k], v, silence_errors=silence_errors)\n        else:\n            try:\n                result[k] = copy.deepcopy(v)\n            except Exception as e:\n                if not silence_errors:\n                    raise e\n\n                result[k] = '<Uncopyable obj:(%s)>' % (v,)\n\n    return result\n\n\ndef circular_reference_label(data, ref_key=None):\n    ref = '.'.join([str(x) for x in ref_key])\n    return '<CircularReference type:(%s) ref:(%s)>' % (type(data).__name__, ref)\n\n\ndef float_nan_label(data):\n    return '<NaN>'\n\n\ndef float_infinity_label(data):\n    if data > 1:\n        return '<Infinity>'\n    else:\n        return '<NegativeInfinity>'\n\n\ndef unencodable_object_label(data):\n    return '<Unencodable type:(%s) base64:(%s)>' % (type(data).__name__,\n                                                    base64.b64encode(data).decode('ascii'))\n\n\ndef undecodable_object_label(data):\n    return '<Undecodable type:(%s) base64:(%s)>' % (type(data).__name__,\n                                                    base64.b64encode(data).decode('ascii'))\n\ntry:\n    from django.utils.functional import SimpleLazyObject\nexcept ImportError:\n    SimpleLazyObject = None\n\n\ndef defaultJSONEncode(o):\n    if SimpleLazyObject and isinstance(o, SimpleLazyObject):\n        if not o._wrapped:\n            o._setup()\n        return o._wrapped\n    return repr(o) + \" is not JSON serializable\"\n"
  },
  {
    "path": "rollbar/lib/_async.py",
    "content": "import asyncio\nimport contextlib\nimport inspect\nimport logging\nimport sys\nfrom unittest import mock\nfrom urllib.parse import urljoin\n\ntry:\n    import httpx\nexcept ImportError:\n    httpx = None\n\nimport rollbar\nfrom rollbar import DEFAULT_TIMEOUT\nfrom rollbar.lib import transport\n\nlog = logging.getLogger(__name__)\n\nALLOWED_HANDLERS = (\n    'async',\n    'httpx',\n)\n\n\nif sys.version_info[:2] == (3, 6):\n    # Backport PEP 567\n    try:\n        import aiocontextvars\n    except ImportError:\n        log.warning(\n            'Python3.6 does not provide the `contextvars` module.'\n            ' Some advanced features may not work as expected.'\n            ' Please upgrade Python or install `aiocontextvars`.'\n        )\n\ntry:\n    from contextvars import ContextVar\nexcept ImportError:\n    ContextVar = None\n\nif ContextVar:\n    _ctx_handler = ContextVar('rollbar-handler', default=None)\nelse:\n    _ctx_handler = None\n\n\nclass RollbarAsyncError(Exception):\n    ...\n\n\nasync def report_exc_info(\n    exc_info=None, request=None, extra_data=None, payload_data=None, level=None, **kw\n):\n    \"\"\"\n    Asynchronously reports an exception to Rollbar, using exc_info (from calling sys.exc_info())\n\n    exc_info: optional, should be the result of calling sys.exc_info(). If omitted, sys.exc_info() will be called here.\n    request: optional, a Starlette, WebOb, Werkzeug-based or Sanic request object.\n    extra_data: optional, will be included in the 'custom' section of the payload\n    payload_data: optional, dict that will override values in the final payload\n                  (e.g. 'level' or 'fingerprint')\n    kw: provided for legacy purposes; unused.\n\n    Example usage:\n\n    rollbar.init(access_token='YOUR_PROJECT_ACCESS_TOKEN')\n\n    async def func():\n        try:\n            do_something()\n        except:\n            await report_exc_info(sys.exc_info(), request, {'foo': 'bar'}, {'level': 'warning'})\n    \"\"\"\n    with AsyncHandler():\n        try:\n            return await call_later(\n                _report_exc_info(\n                    exc_info, request, extra_data, payload_data, level, **kw\n                )\n            )\n        except Exception as e:\n            log.exception('Exception while reporting exc_info to Rollbar. %r', e)\n\n\nasync def report_message(\n    message, level='error', request=None, extra_data=None, payload_data=None, **kw\n):\n    \"\"\"\n    Asynchronously reports an arbitrary string message to Rollbar.\n\n    message: the string body of the message\n    level: level to report at. One of: 'critical', 'error', 'warning', 'info', 'debug'\n    request: the request object for the context of the message\n    extra_data: dictionary of params to include with the message. 'body' is reserved.\n    payload_data: param names to pass in the 'data' level of the payload; overrides defaults.\n    \"\"\"\n\n    with AsyncHandler():\n        try:\n            return await call_later(\n                _report_message(message, level, request, extra_data, payload_data)\n            )\n        except Exception as e:\n            log.exception('Exception while reporting message to Rollbar. %r', e)\n\n\nasync def _report_exc_info(\n    exc_info=None, request=None, extra_data=None, payload_data=None, level=None, **kw\n):\n    return rollbar.report_exc_info(\n        exc_info, request, extra_data, payload_data, level, **kw\n    )\n\n\nasync def _report_message(\n    message, level='error', request=None, extra_data=None, payload_data=None, **kw\n):\n    return rollbar.report_message(message, level, request, extra_data, payload_data)\n\n\nasync def _post_api_httpx(path, payload_str, access_token=None):\n    headers = {'Content-Type': 'application/json'}\n    if access_token is not None:\n        headers['X-Rollbar-Access-Token'] = access_token\n    else:\n        headers['X-Rollbar-Access-Token'] = rollbar.SETTINGS.get('access_token')\n\n    proxy_cfg = {\n        'proxy': rollbar.SETTINGS.get('http_proxy'),\n        'proxy_user': rollbar.SETTINGS.get('http_proxy_user'),\n        'proxy_password': rollbar.SETTINGS.get('http_proxy_password'),\n    }\n    proxies = transport._get_proxy_cfg(proxy_cfg)\n    mounts = None\n    if proxies:\n        mounts = {\n            'http://': httpx.HTTPTransport(proxy=proxies['http']),\n            'https://': httpx.HTTPTransport(proxy=proxies['https']),\n        }\n\n    url = urljoin(rollbar.SETTINGS['endpoint'], path)\n    async with httpx.AsyncClient(\n        mounts=mounts, verify=rollbar.SETTINGS.get('verify_https', True)\n    ) as client:\n        resp = await client.post(\n            url,\n            content=payload_str,\n            headers=headers,\n            timeout=rollbar.SETTINGS.get('timeout', DEFAULT_TIMEOUT),\n        )\n\n    try:\n        return rollbar._parse_response(path, access_token, payload_str, resp)\n    except Exception as e:\n        log.exception('Exception while posting item %r', e)\n\n\nasync def try_report(\n    exc_info=None, request=None, extra_data=None, payload_data=None, level=None, **kw\n):\n    current_handler = rollbar.SETTINGS.get('handler')\n    if not (current_handler in ALLOWED_HANDLERS or current_handler == 'default'):\n        raise RollbarAsyncError('No async handler set.')\n\n    if httpx is None:\n        raise RollbarAsyncError('HTTPX is required')\n\n    return await report_exc_info(\n        exc_info, request, extra_data, payload_data, level, **kw\n    )\n\n\nclass AsyncHandler:\n    def __init__(self):\n        self.global_handler = None\n        self.token = None\n\n    def with_ctx_handler(self):\n        if self.global_handler in ALLOWED_HANDLERS:\n            self.token = _ctx_handler.set(self.global_handler)\n        else:\n            log.warning(\n                'Running coroutines requires async compatible handler. Switching to default async handler.'\n            )\n            self.token = _ctx_handler.set('async')\n\n        return _ctx_handler.get()\n\n    def with_global_handler(self):\n        return self.global_handler\n\n    def __enter__(self):\n        self.global_handler = rollbar.SETTINGS.get('handler')\n\n        if _ctx_handler:\n            return self.with_ctx_handler()\n        else:\n            return self.with_global_handler()\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        if _ctx_handler and self.token:\n            _ctx_handler.reset(self.token)\n\n\ndef get_current_handler():\n    if _ctx_handler is None:\n        return rollbar.SETTINGS.get('handler')\n\n    handler = _ctx_handler.get()\n\n    if handler is None:\n        return rollbar.SETTINGS.get('handler')\n\n    return handler\n\n\ndef call_later(coro):\n    if sys.version_info < (3, 7):\n        return asyncio.ensure_future(coro)\n\n    return asyncio.create_task(coro)\n\n\n# test helpers\n# TODO: move to rollbar.test.async_helper after migrating from unittest\n\n\ndef run(coro):\n    if sys.version_info >= (3, 7):\n        return asyncio.run(coro)\n\n    assert inspect.iscoroutine(coro)\n\n    loop = asyncio.new_event_loop()\n    asyncio.set_event_loop(loop)\n    try:\n        return loop.run_until_complete(coro)\n    finally:\n        loop.close()\n        asyncio.set_event_loop(None)\n\n\ndef async_receive(message):\n    async def receive():\n        return message\n\n    assert message['type'] == 'http.request'\n    return receive\n\n\nasync def coroutine():\n    ...\n\n\nclass BareMiddleware:\n    def __init__(self, app):\n        self.app = app\n\n    async def __call__(self, scope, receive, send):\n        await self.app(scope, receive, send)\n\n\nclass FailingTestASGIApp:\n    async def __call__(self, scope, receive, send):\n        await self.app(scope, receive, send)\n\n    async def app(self, scope, receive, send):\n        raise RuntimeError('Invoked only for testing')\n\n\n# for Python 3.7- compatibility\nclass AsyncMock(mock.MagicMock):\n    async def __call__(self, *args, **kwargs):\n        return super().__call__(*args, **kwargs)\n"
  },
  {
    "path": "rollbar/lib/events.py",
    "content": "EXCEPTION_INFO = 'exception_info'\nMESSAGE = 'message'\nPAYLOAD = 'payload'\n\n_event_handlers = {\n    EXCEPTION_INFO: [],\n    MESSAGE: [],\n    PAYLOAD: []\n}\n\n\ndef _check_type(typ):\n    if typ not in _event_handlers:\n        raise ValueError('Unknown type: %s. Must be one of %s' % (typ, _event_handlers.keys()))\n\n\ndef _add_handler(typ, handler_fn, pos):\n    _check_type(typ)\n\n    pos = pos if pos is not None else -1\n    handlers = _event_handlers[typ]\n\n    try:\n        handlers.index(handler_fn)\n    except ValueError:\n        handlers.insert(pos, handler_fn)\n\n\ndef _remove_handler(typ, handler_fn):\n    _check_type(typ)\n\n    handlers = _event_handlers[typ]\n\n    try:\n        index = handlers.index(handler_fn)\n        handlers.pop(index)\n    except ValueError:\n        pass\n\n\ndef _on_event(typ, target, **kw):\n    _check_type(typ)\n\n    ref = target\n    for handler in _event_handlers[typ]:\n        result = handler(ref, **kw)\n        if result is False:\n            return False\n\n        ref = result\n\n    return ref\n\n\n# Add/remove event handlers\n\ndef add_exception_info_handler(handler_fn, pos=None):\n    _add_handler(EXCEPTION_INFO, handler_fn, pos)\n\n\ndef remove_exception_info_handler(handler_fn):\n    _remove_handler(EXCEPTION_INFO, handler_fn)\n\n\ndef add_message_handler(handler_fn, pos=None):\n    _add_handler(MESSAGE, handler_fn, pos)\n\n\ndef remove_message_handler(handler_fn):\n    _remove_handler(MESSAGE, handler_fn)\n\n\ndef add_payload_handler(handler_fn, pos=None):\n    _add_handler(PAYLOAD, handler_fn, pos)\n\n\ndef remove_payload_handler(handler_fn):\n    _remove_handler(PAYLOAD, handler_fn)\n\n\n# Event handler processing\n\ndef on_exception_info(exc_info, **kw):\n    return _on_event(EXCEPTION_INFO, exc_info, **kw)\n\n\ndef on_message(message, **kw):\n    return _on_event(MESSAGE, message, **kw)\n\n\ndef on_payload(payload, **kw):\n    return _on_event(PAYLOAD, payload, **kw)\n\n\n# Misc\n\ndef reset():\n    for handlers in _event_handlers.values():\n        del handlers[:]\n"
  },
  {
    "path": "rollbar/lib/filters/__init__.py",
    "content": "from rollbar.lib import events\nfrom rollbar.lib.filters.basic import filter_rollbar_ignored_exceptions, filter_by_level\n\n\ndef add_builtin_filters(settings):\n    # exc_info filters\n    events.add_exception_info_handler(filter_rollbar_ignored_exceptions)\n    events.add_exception_info_handler(filter_by_level)\n\n    # message filters\n    events.add_message_handler(filter_by_level)\n"
  },
  {
    "path": "rollbar/lib/filters/basic.py",
    "content": "def filter_rollbar_ignored_exceptions(exc_info, **kw):\n    _, exc, _ = exc_info\n    if getattr(exc, '_rollbar_ignore', False):\n        return False\n\n    return exc_info\n\n\ndef filter_by_level(target, **kw):\n    if 'level' in kw and kw['level'] == 'ignored':\n        return False\n\n    return target\n"
  },
  {
    "path": "rollbar/lib/payload.py",
    "content": "from typing import TypedDict\n\n\nclass Attribute(TypedDict):\n    \"\"\"\n    Represents the `data.attributes` field in the payload, which is used to store session, execution scope information,\n    and other key-value pairs.\n    \"\"\"\n    key: str\n    value: str\n"
  },
  {
    "path": "rollbar/lib/session.py",
    "content": "from __future__ import annotations\n\nimport random\nimport threading\nfrom contextvars import ContextVar\n\nfrom rollbar.lib.payload import Attribute\n\n_context_session: ContextVar[list[Attribute]|None] = ContextVar('rollbar-session', default=None)\n_thread_session: threading.local = threading.local()\n\n\ndef set_current_session(headers: dict[str, str]) -> None:\n    \"\"\"\n    Set current session data.\n\n    The session data should be a dictionary with string keys and string values.\n    \"\"\"\n    session_data = parse_session_request_baggage_headers(headers, generate_missing=True)\n    _context_session.set(session_data)\n    _thread_session.data = session_data\n\n\ndef get_current_session() -> list[Attribute]:\n    \"\"\"\n    Return current session data.\n\n    Do NOT modify the returned session data.\n    \"\"\"\n    session_data = _context_session.get()\n    if session_data is not None:\n        return session_data\n\n    # Fallback to thread local storage for non-async contexts.\n    return getattr(_thread_session, 'data', None) or []\n\n\ndef reset_current_session() -> None:\n    \"\"\"\n    Reset current session data.\n    \"\"\"\n    _context_session.set(None)\n    _thread_session.data = None\n\n\ndef parse_session_request_baggage_headers(headers: dict, generate_missing: bool = False) -> list[Attribute]:\n    \"\"\"\n    Parse the 'baggage' header from the request headers to extract session information. If the 'baggage' header is not\n    present or does not contain the expected keys, a new execution scope ID will be generated and returned as part of\n    the session attributes.\n\n    :param headers: The request headers as a dictionary.\n    :param generate_missing: If True, generates a new execution scope ID if it's missing from the baggage header. If\n                             False, it is not generated. This should only be set to True when the session data is being\n                             stored for the first time in the request context. Generally, `set_current_session()`, and\n                             `get_current_session()` should be used instead.\n    \"\"\"\n    if not headers:\n        if generate_missing:\n            return _build_new_scope_attributes()\n        return []\n\n    baggage_header = None\n\n    # Make sure to handle case-insensitive header keys.\n    for key in headers.keys():\n        if key.lower() == 'baggage':\n            baggage_header = headers[key]\n            break\n\n    if not baggage_header:\n        if generate_missing:\n            return _build_new_scope_attributes()\n        return []\n\n    baggage_items = baggage_header.split(',')\n    baggage_data = []\n    has_scope_id = False\n    for item in baggage_items:\n        if '=' not in item:\n            continue\n        key, value = item.split('=', 1)\n        key = key.strip()\n        if key == 'rollbar.session.id':\n            baggage_data.append({'key': 'session_id', 'value': value.strip()})\n        if key == 'rollbar.execution.scope.id':\n            has_scope_id = True\n            baggage_data.append({'key': 'execution_scope_id', 'value': value.strip()})\n\n    if not baggage_data:\n        if generate_missing:\n            return _build_new_scope_attributes()\n        return []\n\n    # Always ensure we have an execution scope ID, even if the baggage header is present but doesn't contain it.\n    if not has_scope_id and generate_missing:\n        baggage_data.extend(_build_new_scope_attributes())\n\n    return baggage_data\n\n\ndef _build_new_scope_attributes() -> list[Attribute]:\n    \"\"\"\n    Generates a new value for the `rollbar.execution.scope.id` attribute.\n    \"\"\"\n    new_id = _new_scope_id()\n    if new_id is None:\n        return []\n    return [{'key': 'execution_scope_id', 'value': new_id}]\n\n\ndef _new_scope_id() -> str | None:\n    \"\"\"\n    Generate a new random ID with 128 bits of randomness, formatted as a 32-character hexadecimal string. To be used as\n    an execution scope ID.\n    \"\"\"\n    try:\n        # Generate a random integer with exactly 128 random bits\n        num = random.getrandbits(128)\n    except Exception as e:\n        return None\n    return format(num, \"032x\")\n"
  },
  {
    "path": "rollbar/lib/thread_pool.py",
    "content": "import logging\nimport os\nimport sys\nfrom concurrent.futures import ThreadPoolExecutor\n\n_pool = None  # type: ThreadPoolExecutor|None\n\nlog = logging.getLogger(__name__)\n\n\ndef init_pool(max_workers):\n    \"\"\"\n    Creates the thread pool with the max workers.\n\n    :type max_workers: int|None\n    :param max_workers: If max_workers is None it will use the logic from the standard library to calculate the number\n                        of threads. However, we ported the logic from Python 3.5 to earlier versions.\n    \"\"\"\n    if max_workers is None and sys.version_info < (3, 5):\n        max_workers = (os.cpu_count() or 1) * 5\n\n    global _pool\n    _pool = ThreadPoolExecutor(max_workers)\n\n\ndef submit(worker, payload_str, access_token):\n    \"\"\"\n    Submit a new task to the thread pool.\n\n    :type worker: function\n    :type payload_str: str\n    :type access_token: str\n    \"\"\"\n    global _pool\n    if _pool is None:\n        log.warning('pyrollbar: Thead pool not initialized. Please ensure init_pool() is called prior to submit().')\n        return\n    _pool.submit(worker, payload_str, access_token)\n"
  },
  {
    "path": "rollbar/lib/transform.py",
    "content": "from typing import Optional\n\n\nclass Transform(object):\n    depth_first = True\n    priority = 100\n\n    def default(self, o, key=None):\n        return o\n\n    def transform_circular_reference(self, o, key=None, ref_key=None):\n        # By default, we just perform a no-op for circular references.\n        # Subclasses should implement this method to return whatever representation\n        # for the circular reference they need.\n        return self.default(o, key=key)\n\n    def transform_tuple(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_namedtuple(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_list(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_dict(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_number(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_bytes(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_unicode(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_boolean(self, o, key=None):\n        return self.default(o, key=key)\n\n    def transform_path(self, o, key=None):\n        return self.default(str(o), key=key)\n\n    def transform_custom(self, o, key=None):\n        return self.default(o, key=key)\n\n    @staticmethod\n    def rollbar_repr(obj: object) -> Optional[str]:\n        r = None\n        if hasattr(obj, '__rollbar_repr__'):\n            r = obj.__rollbar_repr__()\n            if not isinstance(r, str):\n                raise TypeError(f'__rollbar_repr__ returned non-string (type {type(r)})')\n        return r\n"
  },
  {
    "path": "rollbar/lib/transforms/__init__.py",
    "content": "from collections.abc import Iterable\n\nfrom rollbar.lib import (\n    binary_type,\n    string_types,\n    number_types,\n    traverse,\n)\n# NOTE: Don't remove this import, it would cause a breaking change to the library's API.\n# The `Transform` class was moved out of this file to prevent a cyclical dependency issue.\nfrom rollbar.lib.transform import Transform\nfrom rollbar.lib.transforms.batched import BatchedTransform\n\n_ALLOWED_CIRCULAR_REFERENCE_TYPES = [binary_type, bool, type(None)]\n\nif isinstance(string_types, tuple):\n    _ALLOWED_CIRCULAR_REFERENCE_TYPES.extend(string_types)\nelse:\n    _ALLOWED_CIRCULAR_REFERENCE_TYPES.append(string_types)\n\nif isinstance(number_types, tuple):\n    _ALLOWED_CIRCULAR_REFERENCE_TYPES.extend(number_types)\nelse:\n    _ALLOWED_CIRCULAR_REFERENCE_TYPES.append(number_types)\n\n_ALLOWED_CIRCULAR_REFERENCE_TYPES = tuple(_ALLOWED_CIRCULAR_REFERENCE_TYPES)\n\n\ndef transform(obj, transforms, key=None, batch_transforms=False):\n    if isinstance(transforms, Transform):\n        transforms = [transforms]\n\n    if batch_transforms:\n        transforms = [BatchedTransform(transforms)]\n\n    for transform in transforms:\n        if not isinstance(transform, Transform):\n            continue\n        obj = _transform(obj, transform, key=key)\n\n    return obj\n\n\ndef _transform(obj, transform, key=None):\n    key = key or ()\n\n    def do_transform(type_name, val, key=None, **kw):\n        fn = getattr(transform, \"transform_%s\" % type_name, transform.transform_custom)\n        val = fn(val, key=key, **kw)\n\n        return val\n\n    def string_handler(s, key=None):\n        if isinstance(s, bytes):\n            return do_transform(\"bytes\", s, key=key)\n        elif isinstance(s, str):\n            return do_transform(\"unicode\", s, key=key)\n\n    def default_handler(o, key=None):\n        if isinstance(o, bool):\n            return do_transform(\"boolean\", o, key=key)\n\n        # There is a quirk in the current version (1.1.6) of the enum\n        # backport enum34 which causes it to not have the same\n        # behavior as Python 3.4+. One way to identify IntEnums is that\n        # they are instances of numbers but not number types.\n        if isinstance(o, number_types):\n            if type(o) not in number_types:\n                return do_transform(\"custom\", o, key=key)\n            else:\n                return do_transform(\"number\", o, key=key)\n\n        return do_transform(\"custom\", o, key=key)\n\n    handlers = {\n        \"string_handler\": string_handler,\n        \"tuple_handler\": lambda o, key=None: do_transform(\"tuple\", o, key=key),\n        \"namedtuple_handler\": lambda o, key=None: do_transform(\n            \"namedtuple\", o, key=key\n        ),\n        \"list_handler\": lambda o, key=None: do_transform(\"list\", o, key=key),\n        \"set_handler\": lambda o, key=None: do_transform(\"set\", o, key=key),\n        \"mapping_handler\": lambda o, key=None: do_transform(\"dict\", o, key=key),\n        \"path_handler\": lambda o, key=None: do_transform(\"path\", o, key=key),\n        \"circular_reference_handler\": lambda o, key=None, ref_key=None: do_transform(\n            \"circular_reference\", o, key=key, ref_key=ref_key\n        ),\n        \"default_handler\": default_handler,\n        \"allowed_circular_reference_types\": _ALLOWED_CIRCULAR_REFERENCE_TYPES,\n    }\n\n    return traverse.traverse(obj, key=key, depth_first=transform.depth_first, **handlers)\n\n\n__all__ = [\"transform\", \"Transform\"]\n"
  },
  {
    "path": "rollbar/lib/transforms/batched.py",
    "content": "from rollbar.lib.transform import Transform\nfrom rollbar.lib import (\n    number_types,\n    type_info,\n)\n\n\ndef do_transform(transform, type_name, val, key=None, **kw):\n    fn = getattr(transform, \"transform_%s\" % type_name, transform.transform_custom)\n    val = fn(val, key=key, **kw)\n\n    return val\n\n\ndef string_handler(transform, s, key=None):\n    if isinstance(s, bytes):\n        return do_transform(transform, \"bytes\", s, key=key)\n    elif isinstance(s, str):\n        return do_transform(transform, \"unicode\", s, key=key)\n\n\ndef default_handler(transform, o, key=None):\n    if isinstance(o, bool):\n        return do_transform(transform, \"boolean\", o, key=key)\n\n    # There is a quirk in the current version (1.1.6) of the enum\n    # backport enum34 which causes it to not have the same\n    # behavior as Python 3.4+. One way to identify IntEnums is that\n    # they are instances of numbers but not number types.\n    if isinstance(o, number_types):\n        if type(o) not in number_types:\n            return do_transform(transform, \"custom\", o, key=key)\n        else:\n            return do_transform(transform, \"number\", o, key=key)\n\n    return do_transform(transform, \"custom\", o, key=key)\n\n\nhandlers = {\n    type_info.STRING: string_handler,\n    type_info.TUPLE: lambda transform, o, key=None: do_transform(\n        transform, \"tuple\", o, key=key\n    ),\n    type_info.NAMEDTUPLE: lambda transform, o, key=None: do_transform(\n        transform, \"namedtuple\", o, key=key\n    ),\n    type_info.LIST: lambda transform, o, key=None: do_transform(\n        transform, \"list\", o, key=key\n    ),\n    type_info.SET: lambda transform, o, key=None: do_transform(\n        transform, \"set\", o, key=key\n    ),\n    type_info.MAPPING: lambda transform, o, key=None: do_transform(\n        transform, \"dict\", o, key=key\n    ),\n    type_info.CIRCULAR: lambda transform, o, key=None, ref_key=None: do_transform(\n        transform, \"circular_reference\", o, key=key, ref_key=ref_key\n    ),\n    type_info.DEFAULT: default_handler,\n}\n\n\nclass BatchedTransform(Transform):\n    def __init__(self, transforms):\n        super(BatchedTransform, self).__init__()\n        self._transforms = transforms\n\n    def default(self, o, key=None):\n        for transform in self._transforms:\n            node_type = type_info.get_type(o)\n            handler = handlers.get(node_type, handlers.get(type_info.DEFAULT))\n            o = handler(transform, o, key=key)\n\n        return o\n\n\n__all__ = [\"BatchedTransform\"]\n"
  },
  {
    "path": "rollbar/lib/transforms/scrub.py",
    "content": "import random\n\nfrom rollbar.lib import build_key_matcher\nfrom rollbar.lib.transform import Transform\n\n\nclass ScrubTransform(Transform):\n    suffix_matcher = None\n    priority = 40\n    def __init__(self, suffixes=None, redact_char='*', randomize_len=True):\n        super(ScrubTransform, self).__init__()\n        if suffixes is not None and len(suffixes) > 0:\n            self.suffix_matcher = build_key_matcher(suffixes, type='suffix')\n        self.redact_char = redact_char\n        self.randomize_len = randomize_len\n\n    def in_scrub_fields(self, key):\n        if self.suffix_matcher is None:\n            return False\n        return self.suffix_matcher(key)\n\n    def redact(self, val):\n        if self.randomize_len:\n            _len = random.randint(3, 20)\n        else:\n            try:\n                _len = len(val)\n            except:\n                _len = len(str(val))\n\n        return self.redact_char * _len\n\n    def default(self, o, key=None):\n        if self.in_scrub_fields(key):\n            return self.redact(o)\n\n        return o\n\n\n__all__ = ['ScrubTransform']\n"
  },
  {
    "path": "rollbar/lib/transforms/scrub_redact.py",
    "content": "from rollbar.lib.transforms.scrub import ScrubTransform\n\n\nclass RedactRef(object):\n    pass\n\n\nREDACT_REF = RedactRef()\n\n\nclass ScrubRedactTransform(ScrubTransform):\n    priority = 20\n    def default(self, o, key=None):\n        if o is REDACT_REF:\n            return self.redact(o)\n\n        return super(ScrubRedactTransform, self).default(o, key=key)\n\n\n__all__ = ['ScrubRedactTransform']\n"
  },
  {
    "path": "rollbar/lib/transforms/scruburl.py",
    "content": "import re\nfrom urllib.parse import urlsplit, urlencode, urlunsplit, parse_qs\n\nfrom rollbar.lib import string_types, binary_type\nfrom rollbar.lib.transforms.scrub import ScrubTransform\n\n\n_starts_with_auth_re = re.compile(r'^[a-zA-Z0-9-_]*(:[^@/]+)?@')\n\n\nclass ScrubUrlTransform(ScrubTransform):\n    priority = 50\n    def __init__(self,\n                 suffixes=None,\n                 scrub_username=False,\n                 scrub_password=True,\n                 params_to_scrub=None,\n                 redact_char='-',\n                 randomize_len=True):\n\n        super(ScrubUrlTransform, self).__init__(suffixes=suffixes,\n                                                redact_char=redact_char,\n                                                randomize_len=randomize_len)\n        self.scrub_username = scrub_username\n        self.scrub_password = scrub_password\n        self.params_to_scrub = {x.lower() for x in params_to_scrub or []}\n\n    def in_scrub_fields(self, key):\n        # Returning True here because we want to scrub URLs out of\n        # every string, not just ones that we know the key for.\n        return True\n\n    def redact(self, url_string):\n        _redact = super(ScrubUrlTransform, self).redact\n\n        missing_colon_double_slash = False\n\n        if _starts_with_auth_re.match(url_string):\n            missing_colon_double_slash = True\n            url_string = '//%s' % url_string\n\n        try:\n            url_parts = urlsplit(url_string)\n            qs_params = parse_qs(url_parts.query, keep_blank_values=True)\n        except:\n            # This isn't a URL, return url_string which is a no-op\n            # for this transform\n            return url_string\n\n        netloc = url_parts.netloc\n\n        # If there's no netloc, give up\n        if not netloc:\n            return url_string\n\n        for qs_param, vals in qs_params.items():\n            if qs_param.lower() in self.params_to_scrub:\n                vals2 = [_redact(x) for x in vals]\n                qs_params[qs_param] = vals2\n\n        scrubbed_qs = urlencode(qs_params, doseq=True)\n\n        if self.scrub_username and url_parts.username:\n            redacted_username = _redact(url_parts.username)\n            netloc = netloc.replace(url_parts.username, redacted_username)\n\n        if self.scrub_password and url_parts.password:\n            redacted_pw = _redact(url_parts.password)\n            netloc = netloc.replace(url_parts.password, redacted_pw)\n\n        scrubbed_url = (url_parts.scheme,\n                        netloc,\n                        url_parts.path,\n                        scrubbed_qs,\n                        url_parts.fragment)\n\n        scrubbed_url_string = urlunsplit(scrubbed_url)\n\n        if missing_colon_double_slash:\n            scrubbed_url_string = scrubbed_url_string.lstrip('://')\n\n        return scrubbed_url_string\n\n    def default(self, o, key=None):\n        # Change the default behavior because we are only interested\n        # in scrubbing strings.\n        if isinstance(o, string_types) or isinstance(o, binary_type):\n            return super(ScrubUrlTransform, self).default(o, key=key)\n\n        return o\n\n\n__all__ = ['ScrubUrlTransform']\n"
  },
  {
    "path": "rollbar/lib/transforms/serializable.py",
    "content": "import math\n\nfrom rollbar.lib import binary_type, string_types\nfrom rollbar.lib import (\n    circular_reference_label, float_infinity_label, float_nan_label,\n    undecodable_object_label, unencodable_object_label)\n\nfrom rollbar.lib.transform import Transform\n\n\nclass SerializableTransform(Transform):\n    priority = 30\n    def __init__(self, safe_repr=True, safelist_types=None):\n        super(SerializableTransform, self).__init__()\n        self.safe_repr = safe_repr\n        self.safelist = set(safelist_types or [])\n\n    def transform_circular_reference(self, o, key=None, ref_key=None):\n        return circular_reference_label(o, ref_key)\n\n    def transform_namedtuple(self, o, key=None):\n        tuple_dict = o._asdict()\n        transformed_dict = self.transform_dict(tuple_dict, key=key)\n        new_vals = []\n        for field in tuple_dict:\n            new_vals.append(transformed_dict[field])\n\n        return '<%s>' % str(o._make(new_vals))\n\n    def transform_number(self, o, key=None):\n        if math.isnan(o):\n            return float_nan_label(o)\n        elif math.isinf(o):\n            return float_infinity_label(o)\n        else:\n            return o\n\n    def transform_bytes(self, o, key=None):\n        try:\n            o.decode('utf8')\n        except UnicodeDecodeError:\n            return undecodable_object_label(o)\n        else:\n            return repr(o)\n\n    def transform_unicode(self, o, key=None):\n        try:\n            o.encode('utf8')\n        except UnicodeEncodeError:\n            return unencodable_object_label(o)\n        else:\n            return o\n\n    def transform_dict(self, o, key=None):\n        ret = {}\n        for k, v in o.items():\n            if isinstance(k, string_types) or isinstance(k, binary_type):\n                if isinstance(k, bytes):\n                    new_k = self.transform_bytes(k)\n                else:\n                    new_k = self.transform_unicode(k)\n            else:\n                new_k = str(k)\n\n            ret[new_k] = v\n\n        return super(SerializableTransform, self).transform_dict(ret, key=key)\n\n    def transform_custom(self, o, key=None):\n        if o is None:\n            return None\n\n        # Best to be very careful when we call user code in the middle of\n        # preparing a stack trace. So we put a try/except around it all.\n        try:\n            # If the object has a __rollbar_repr__() method, use it.\n            custom = Transform.rollbar_repr(o)\n            if custom is not None:\n                return custom\n\n            if any(filter(lambda x: isinstance(o, x), self.safelist)):\n                try:\n                    return repr(o)\n                except TypeError:\n                    pass\n\n            # If self.safe_repr is False, use repr() to serialize the object\n            if not self.safe_repr:\n                try:\n                    return repr(o)\n                except TypeError:\n                    pass\n\n            # Otherwise, just use the type name\n            return str(type(o))\n\n        except Exception as e:\n            exc_str = ''\n            try:\n                exc_str = str(e)\n            except Exception as e2:\n                exc_str = '[%s while calling str(%s)]' % (\n                    e2.__class__.__name__, e.__class__.__name__)\n            return '<%s in %s.__repr__: %s>' % (\n                e.__class__.__name__, o.__class__.__name__, exc_str)\n\n\n__all__ = ['SerializableTransform']\n"
  },
  {
    "path": "rollbar/lib/transforms/shortener.py",
    "content": "from array import array\nimport collections\nimport itertools\nimport reprlib\n\nfrom collections.abc import Mapping\nfrom typing import Union, Tuple\n\nfrom rollbar.lib import (\n    integer_types, key_in, key_depth, sequence_types,\n    string_types)\nfrom rollbar.lib.transform import Transform\n\n\n_type_name_mapping = {\n    'string': string_types,\n    'long': integer_types,\n    'mapping': Mapping,\n    'list': list,\n    'tuple': tuple,\n    'set': set,\n    'frozenset': frozenset,\n    'array': array,\n    'deque': collections.deque,\n    'other': None\n}\n\n\ndef _max_left_right(max_len: int, seperator_len: int) -> Tuple[int, int]:\n    left = max(0, (max_len-seperator_len)//2)\n    right = max(0, max_len-seperator_len-left)\n    return left, right\n\n\ndef shorten_array(obj: array, max_len: int) -> array:\n    if len(obj) <= max_len:\n        return obj\n\n    return obj[:max_len]\n\n\ndef shorten_bytes(obj: bytes, max_len: int) -> bytes:\n    if len(obj) <= max_len:\n        return obj\n\n    return obj[:max_len]\n\n\ndef shorten_deque(obj: collections.deque, max_len: int) -> collections.deque:\n    if len(obj) <= max_len:\n        return obj\n\n    return collections.deque(itertools.islice(obj, max_len))\n\n\ndef shorten_frozenset(obj: frozenset, max_len: int) -> frozenset:\n    if len(obj) <= max_len:\n        return obj\n\n    return frozenset([elem for i, elem in enumerate(obj) if i < max_len] + ['...'])\n\n\ndef shorten_int(obj: int, max_len: int) -> Union[int, str]:\n    s = repr(obj)\n    if len(s) <= max_len:\n        return obj\n\n    left, right = _max_left_right(max_len, 3)\n    return s[:left] + '...' + s[len(s)-right:]\n\n\ndef shorten_list(obj: list, max_len: int) -> list:\n    if len(obj) <= max_len:\n        return obj\n\n    return obj[:max_len] + ['...']\n\n\ndef shorten_mapping(obj: Union[dict, Mapping], max_keys: int) -> dict:\n    if len(obj) <= max_keys:\n        return obj\n\n    return {k: obj[k] for k in itertools.islice(obj.keys(), max_keys)}\n\n\ndef shorten_set(obj: set, max_len: int) -> set:\n    if len(obj) <= max_len:\n        return obj\n\n    return set([elem for i, elem in enumerate(obj) if i < max_len] + ['...'])\n\n\ndef shorten_string(obj: str, max_len: int) -> str:\n    if len(obj) <= max_len:\n        return obj\n\n    left, right = _max_left_right(max_len, 3)\n    return obj[:left] + '...' + obj[len(obj)-right:]\n\n\ndef shorten_tuple(obj: tuple, max_len: int) -> tuple:\n    if len(obj) <= max_len:\n        return obj\n\n    return obj[:max_len] + ('...',)\n\n\nclass ShortenerTransform(Transform):\n    depth_first = False\n    priority = 10\n\n    def __init__(self, safe_repr=True, keys=None, **sizes):\n        super(ShortenerTransform, self).__init__()\n        self.safe_repr = safe_repr\n        self.keys = keys\n        self._repr = reprlib.Repr()\n\n        for name, size in sizes.items():\n            setattr(self._repr, name, size)\n\n    def _get_max_size(self, obj):\n        for name, _type in _type_name_mapping.items():\n            # Special case for dicts since we are using collections.abc.Mapping\n            # to provide better type checking for dict-like objects\n            if name == 'mapping':\n                name = 'dict'\n\n            if _type and isinstance(obj, _type):\n                return getattr(self._repr, 'max%s' % name)\n\n        return self._repr.maxother\n\n    def _shorten(self, val):\n        max_size = self._get_max_size(val)\n\n        if isinstance(val, array):\n            return shorten_array(val, max_size)\n        if isinstance(val, bytes):\n            return shorten_bytes(val, max_size)\n        if isinstance(val, collections.deque):\n            return shorten_deque(val, max_size)\n        if isinstance(val, (dict, Mapping)):\n            return shorten_mapping(val, max_size)\n        if isinstance(val, float):\n            return val\n        if isinstance(val, frozenset):\n            return shorten_frozenset(val, max_size)\n        if isinstance(val, int):\n            return shorten_int(val, max_size)\n        if isinstance(val, list):\n            return shorten_list(val, max_size)\n        if isinstance(val, set):\n            return shorten_set(val, max_size)\n        if isinstance(val, str):\n            return shorten_string(val, max_size)\n        if isinstance(val, tuple):\n            return shorten_tuple(val, max_size)\n\n        return self._shorten_other(val)\n\n    def _shorten_other(self, obj):\n        if obj is None:\n            return None\n\n        # If the object has a __rollbar_repr__() method, use it.\n        custom = Transform.rollbar_repr(obj)\n        if custom is not None:\n            return custom\n\n        if self.safe_repr:\n            obj = str(obj)\n\n        return self._repr.repr(obj)\n\n    def _should_shorten(self, val, key):\n        if not key:\n            return False\n\n        return key_in(key, self.keys)\n\n    def _should_drop(self, val, key) -> bool:\n        if not key:\n            return False\n\n        max_depth = key_depth(key, self.keys)\n        if max_depth == 0:\n            return False\n\n        return (max_depth + self._repr.maxlevel) <= len(key)\n\n    def default(self, o, key=None):\n        if self._should_drop(o, key):\n            if isinstance(o, (dict, Mapping)):\n                return {'...': '...'}\n            if isinstance(o, sequence_types):\n                return ['...']\n\n        if self._should_shorten(o, key):\n            return self._shorten(o)\n\n        return super(ShortenerTransform, self).default(o, key=key)\n\n\n__all__ = ['ShortenerTransform']\n"
  },
  {
    "path": "rollbar/lib/transport.py",
    "content": "from typing import Optional\n\nimport requests\nimport threading\n\n\n_local = threading.local()\n\n\ndef _session():\n    if hasattr(_local, 'session'):\n        return _local.session\n    _local.session = requests.Session()\n    return _local.session\n\n\ndef _get_proxy_cfg(kw: dict) -> Optional[dict]:\n    proxy = kw.pop('proxy', None)\n    proxy_user = kw.pop('proxy_user', None)\n    proxy_password = kw.pop('proxy_password', None)\n    if proxy and proxy_user and proxy_password:\n        return {\n            'http': f'http://{proxy_user}:{proxy_password}@{proxy}',\n            'https': f'http://{proxy_user}:{proxy_password}@{proxy}',\n        }\n    elif proxy:\n        return {\n            'http': f'http://{proxy}',\n            'https': f'http://{proxy}',\n        }\n\n\ndef configure_pool(**kw):\n    keys = ['pool_connections', 'pool_maxsize', 'max_retries']\n    args = {k: kw[k] for k in keys if kw.get(k, None) is not None}\n    if len(args) == 0:\n        return\n    https_adapter = requests.adapters.HTTPAdapter(**args)\n    http_adapter = requests.adapters.HTTPAdapter(**args)\n    _session().mount('https://', https_adapter)\n    _session().mount('http://', http_adapter)\n\n\ndef post(*args, **kw):\n    proxies = _get_proxy_cfg(kw)\n    return _session().post(*args, proxies=proxies, **kw)\n\n\ndef get(*args, **kw):\n    proxies = _get_proxy_cfg(kw)\n    return _session().get(*args, proxies=proxies, **kw)\n\n\n__all__ = ['post', 'get', 'configure_pool']\n"
  },
  {
    "path": "rollbar/lib/traverse.py",
    "content": "import logging\nfrom pathlib import Path\n\nfrom rollbar.lib import binary_type, string_types, circular_reference_label\n\n# NOTE: Don't remove this line of code as it would cause a breaking change\n# to the library's API. The items imported here were originally in this file\n# but were moved to a new file for easier use elsewhere.\nfrom rollbar.lib.type_info import (\n    get_type,\n    CIRCULAR,\n    DEFAULT,\n    MAPPING,\n    TUPLE,\n    NAMEDTUPLE,\n    LIST,\n    SET,\n    STRING,\n    PATH,\n)\n\n\nlog = logging.getLogger(__name__)\n\n\ndef _noop_circular(a, **kw):\n    return circular_reference_label(a, ref_key=kw.get(\"ref_key\"))\n\n\ndef _noop(a, **_):\n    return a\n\n\ndef _noop_tuple(a, **_):\n    return tuple(a)\n\n\ndef _noop_namedtuple(a, **_):\n    return a._make(a)\n\n\ndef _noop_list(a, **_):\n    return list(a)\n\n\ndef _noop_set(a, **_):\n    return set(a)\n\n\ndef _noop_mapping(a, **_):\n    return dict(a)\n\ndef _noop_path(a, **_):\n    return Path(a)\n\n\n_default_handlers = {\n    CIRCULAR: _noop_circular,\n    DEFAULT: _noop,\n    STRING: _noop,\n    TUPLE: _noop_tuple,\n    NAMEDTUPLE: _noop_namedtuple,\n    LIST: _noop_list,\n    SET: _noop_set,\n    PATH: _noop_path,\n    MAPPING: _noop_mapping,\n}\n\n\ndef traverse(\n    obj,\n    key=(),\n    string_handler=_default_handlers[STRING],\n    tuple_handler=_default_handlers[TUPLE],\n    namedtuple_handler=_default_handlers[NAMEDTUPLE],\n    list_handler=_default_handlers[LIST],\n    set_handler=_default_handlers[SET],\n    mapping_handler=_default_handlers[MAPPING],\n    path_handler=_default_handlers[PATH],\n    default_handler=_default_handlers[DEFAULT],\n    circular_reference_handler=_default_handlers[CIRCULAR],\n    allowed_circular_reference_types=None,\n    memo=None,\n    depth_first=True,\n    **custom_handlers\n):\n    memo = memo or {}\n    obj_id = id(obj)\n    obj_type = get_type(obj)\n\n    ref_key = memo.get(obj_id)\n    if ref_key:\n        if not allowed_circular_reference_types or not isinstance(\n            obj, allowed_circular_reference_types\n        ):\n            return circular_reference_handler(obj, key=key, ref_key=ref_key)\n\n    memo[obj_id] = key\n\n    kw = {\n        \"string_handler\": string_handler,\n        \"tuple_handler\": tuple_handler,\n        \"namedtuple_handler\": namedtuple_handler,\n        \"list_handler\": list_handler,\n        \"set_handler\": set_handler,\n        \"mapping_handler\": mapping_handler,\n        \"path_handler\": path_handler,\n        \"default_handler\": default_handler,\n        \"circular_reference_handler\": circular_reference_handler,\n        \"allowed_circular_reference_types\": allowed_circular_reference_types,\n        \"memo\": memo,\n        \"depth_first\": depth_first,\n    }\n    kw.update(custom_handlers)\n\n    try:\n        if obj_type is STRING:\n            return string_handler(obj, key=key)\n        elif obj_type is TUPLE:\n            if depth_first:\n                return tuple_handler(\n                    tuple(\n                        traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(obj)\n                    ),\n                    key=key,\n                )\n            # Breadth first\n            return tuple(traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(tuple_handler(obj, key=key)))\n        elif obj_type is NAMEDTUPLE:\n            if depth_first:\n                return namedtuple_handler(\n                    obj._make(\n                        traverse(v, key=key + (k,), **kw)\n                        for k, v in obj._asdict().items()\n                    ),\n                    key=key,\n                )\n            # Breadth first\n            return obj._make(traverse(v, key=key + (k,), **kw) for k, v in namedtuple_handler(obj, key=key)._asdict().items())\n        elif obj_type is LIST:\n            if depth_first:\n                return list_handler(\n                    [traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(obj)],\n                    key=key,\n                )\n            # Breadth first\n            return [traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(list_handler(obj, key=key))]\n        elif obj_type is SET:\n            if depth_first:\n                return set_handler(\n                    {traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(obj)},\n                    key=key,\n                )\n            # Breadth first\n            return {traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(set_handler(obj, key=key))}\n        elif obj_type is MAPPING:\n            if depth_first:\n                return mapping_handler(\n                    {k: traverse(v, key=key + (k,), **kw) for k, v in obj.items()},\n                    key=key,\n                )\n            # Breadth first\n            return {k: traverse(v, key=key + (k,), **kw) for k, v in mapping_handler(obj, key=key).items()}\n        elif obj_type is PATH:\n            return path_handler(obj, key=key)\n        elif obj_type is DEFAULT:\n            for handler_type, handler in custom_handlers.items():\n                if isinstance(obj, handler_type):\n                    return handler(obj, key=key)\n    except:\n        # use the default handler for unknown object types\n        log.debug(\n            \"Exception while traversing object using type-specific \"\n            \"handler. Switching to default handler.\",\n            exc_info=True,\n        )\n\n    return default_handler(obj, key=key)\n\n\n__all__ = [\"traverse\"]\n"
  },
  {
    "path": "rollbar/lib/type_info.py",
    "content": "from rollbar.lib import binary_type, string_types\n\n\nfrom collections.abc import Mapping, Sequence, Set\nfrom pathlib import Path\n\n\nCIRCULAR = -1\nDEFAULT = 0\nMAPPING = 1\nTUPLE = 2\nNAMEDTUPLE = 3\nLIST = 4\nSET = 5\nSTRING = 6\nPATH = 7\n\n\ndef get_type(obj):\n    if isinstance(obj, (string_types, binary_type)):\n        return STRING\n\n    if isinstance(obj, Mapping):\n        return MAPPING\n\n    if isinstance(obj, tuple):\n        if hasattr(obj, \"_fields\"):\n            return NAMEDTUPLE\n\n        return TUPLE\n\n    if isinstance(obj, set):\n        return SET\n\n    if isinstance(obj, Sequence):\n        return LIST\n\n    if isinstance(obj, Path):\n        return PATH\n\n    return DEFAULT\n\n\n__all__ = [\n    \"CIRCULAR\",\n    \"DEFAULT\",\n    \"MAPPING\",\n    \"TUPLE\",\n    \"NAMEDTUPLE\",\n    \"LIST\",\n    \"SET\",\n    \"STRING\",\n    \"PATH\",\n    \"get_type\",\n]\n"
  },
  {
    "path": "rollbar/logger.py",
    "content": "\"\"\"\nHooks for integrating with the python logging framework.\n\nUsage:\n    import logging\n    from rollbar.logger import RollbarHandler\n\n    rollbar.init('ACCESS_TOKEN', 'ENVIRONMENT')\n\n    logger = logging.getLogger(__name__)\n    logger.setLevel(logging.DEBUG)\n\n    # report ERROR and above to Rollbar\n    rollbar_handler = RollbarHandler()\n    rollbar_handler.setLevel(logging.ERROR)\n\n    # attach the handlers to the root logger\n    logger.addHandler(rollbar_handler)\n\n\"\"\"\nimport logging\nimport threading\n\nfrom logging.config import ConvertingDict, ConvertingList, ConvertingTuple\n\nimport rollbar\n\n# hack to fix backward compatibility in Python3\ntry:\n    from logging import _checkLevel\nexcept ImportError:\n    _checkLevel = lambda lvl: lvl\n\n\nEXCLUDE_RECORD_KEYS = {\n    # Attributes that are disallowed in `logging.Logger.makeRecord`\n    'asctime',\n    'message',\n    # Attributes that are used internally by pyrollbar\n    'extra_data',\n    'payload_data',\n    'request',\n    *vars(logging.makeLogRecord({})).keys(),\n}\n\n\ndef resolve_logging_types(obj):\n    if isinstance(obj, (dict, ConvertingDict)):\n        return {k: resolve_logging_types(v) for k, v in obj.items()}\n    elif isinstance(obj, (list, ConvertingList)):\n        return [resolve_logging_types(i) for i in obj]\n    elif isinstance(obj, (tuple, ConvertingTuple)):\n        return tuple(resolve_logging_types(i) for i in obj)\n\n    return obj\n\n\nclass RollbarHandler(logging.Handler):\n    SUPPORTED_LEVELS = set(('debug', 'info', 'warning', 'error', 'critical'))\n\n    _history = threading.local()\n\n    def __init__(self,\n                 access_token=None,\n                 environment=None,\n                 level=logging.INFO,\n                 history_size=10,\n                 history_level=logging.DEBUG,\n                 **kw):\n\n        logging.Handler.__init__(self)\n\n        if access_token is not None:\n            rollbar.init(\n                access_token, environment,\n                allow_logging_basic_config=False,   # a handler shouldn't configure the root logger\n                **resolve_logging_types(kw))\n\n        self.notify_level = _checkLevel(level)\n\n        self.history_size = history_size\n        if history_size > 0:\n            self._history.records = []\n\n        self.setHistoryLevel(history_level)\n\n    def setLevel(self, level):\n        \"\"\"\n        Override so we set the effective level for which\n        log records we notify Rollbar about instead of which\n        records we save to the history.\n        \"\"\"\n        self.notify_level = _checkLevel(level)\n\n    def setHistoryLevel(self, level):\n        \"\"\"\n        Use this method to determine which records we record history\n        for. Use setLevel() to determine which level we report records\n        to Rollbar for.\n        \"\"\"\n        logging.Handler.setLevel(self, level)\n\n    def emit(self, record):\n        # If the record came from Rollbar's own logger don't report it\n        # to Rollbar\n        if record.name == rollbar.__log_name__:\n            return\n\n        level = record.levelname.lower()\n\n        if level not in self.SUPPORTED_LEVELS:\n            return\n\n        exc_info = record.exc_info\n\n        extra_data = {\n            'args': record.args,\n            'record': {\n                'created': record.created,\n                'funcName': record.funcName,\n                'lineno': record.lineno,\n                'module': record.module,\n                'name': record.name,\n                'pathname': record.pathname,\n                'process': record.process,\n                'processName': record.processName,\n                'relativeCreated': record.relativeCreated,\n                'thread': record.thread,\n                'threadName': record.threadName\n            }\n        } | {  # include any extras\n            k: v for k, v in vars(record).items()\n            if k not in EXCLUDE_RECORD_KEYS\n        } | getattr(record, 'extra_data', {})  # include historical extra_data\n\n        payload_data = getattr(record, 'payload_data', {})\n\n        self._add_history(record, payload_data)\n\n        # after we've added the history data, check to see if the\n        # notify level is satisfied\n        if record.levelno < self.notify_level:\n            return\n\n        # Wait until we know we're going to send a report before trying to\n        # load the request\n        request = getattr(record, \"request\", None) or rollbar.get_request()\n\n        # Rather than copy the log record and disable exception and stack trace\n        # formatting, this does the same steps to prepare the log record\n        # as `logging.Formatter.format` does before calling\n        # `logging.Formatter.formatMessage`.\n        formatter = self.formatter or logging._defaultFormatter\n        record.message = record.getMessage()\n        if formatter.usesTime():\n            record.asctime = formatter.formatTime(record, formatter.datefmt)\n\n        message = formatter.formatMessage(record)\n\n        uuid = None\n        try:\n            # when not in an exception handler, exc_info == (None, None, None)\n            if exc_info and exc_info[0]:\n                if record.msg:\n                    message_template = {\n                        'body': {\n                            'trace': {'exception': {'description': message}}\n                        }\n                    }\n                    payload_data = rollbar.dict_merge(\n                        payload_data, message_template, silence_errors=True)\n\n                uuid = rollbar.report_exc_info(exc_info,\n                                               level=level,\n                                               request=request,\n                                               extra_data=extra_data,\n                                               payload_data=payload_data)\n            else:\n                uuid = rollbar.report_message(message,\n                                              level=level,\n                                              request=request,\n                                              extra_data=extra_data,\n                                              payload_data=payload_data)\n        except:\n            self.handleError(record)\n        else:\n            if uuid:\n                record.rollbar_uuid = uuid\n\n    def _add_history(self, record, payload_data):\n        if hasattr(self._history, 'records'):\n            records = self._history.records\n            history = list(records[-self.history_size:])\n\n            if history:\n                history_data = [self._build_history_data(r) for r in history]\n                payload_data.setdefault('server', {})['history'] = history_data\n\n            records.append(record)\n\n            # prune the messages if we have too many\n            self._history.records = list(records[-self.history_size:])\n\n    def _build_history_data(self, record):\n        data = {'timestamp': record.created,\n                'format': record.msg,\n                'args': record.args}\n\n        if hasattr(record, 'rollbar_uuid'):\n            data['uuid'] = record.rollbar_uuid\n\n        return data\n"
  },
  {
    "path": "rollbar/test/__init__.py",
    "content": "import unittest\n\n\nSNOWMAN = b'\\xe2\\x98\\x83'\nSNOWMAN_UNICODE = SNOWMAN.decode('utf8')\n\n\nclass BaseTest(unittest.TestCase):\n    pass\n\n\ndef discover():\n    loader = unittest.TestLoader()\n    suite = loader.discover(__name__)\n    return suite\n"
  },
  {
    "path": "rollbar/test/asgi_tests/__init__.py",
    "content": "import sys\nimport unittest\n\n\ndef _load_tests(loader, tests, pattern):\n    return unittest.TestSuite()\n\n\nif sys.version_info < (3, 5):\n    load_tests = _load_tests\n"
  },
  {
    "path": "rollbar/test/asgi_tests/test_integration.py",
    "content": "import unittest\nimport sys\n\nfrom rollbar.test import BaseTest\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 5)\n\n\n@unittest.skipUnless(ALLOWED_PYTHON_VERSION, 'ASGI implementation requires Python3.5+')\nclass IntegrationTest(BaseTest):\n    def test_should_integrate_if__integrate_defined(self):\n        from rollbar.contrib.asgi.integration import IntegrationBase\n\n        called = False  # cannot patch local objects\n\n        class Integration(IntegrationBase):\n            def _integrate(self):\n                nonlocal called\n                called = True\n\n        self.assertTrue(hasattr(Integration, '_integrate'))\n\n        obj = Integration()\n\n        self.assertTrue(called)\n        self.assertTrue(hasattr(obj, '_integrate'))\n\n    def test_should_not_fail_if__integrate_not_exists(self):\n        from rollbar.contrib.asgi.integration import IntegrationBase\n\n        class WrongIntegration(IntegrationBase):\n            ...\n\n        self.assertFalse(hasattr(WrongIntegration, '_integrate'))\n\n        obj = WrongIntegration()\n\n        self.assertFalse(hasattr(obj, '_integrate'))\n"
  },
  {
    "path": "rollbar/test/asgi_tests/test_middleware.py",
    "content": "import copy\nimport importlib\nimport sys\n\nfrom unittest import mock\n\nimport unittest\n\nimport rollbar\nfrom rollbar.lib._async import AsyncMock\nfrom rollbar.test import BaseTest\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 5)\nASYNC_REPORT_ENABLED = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(ALLOWED_PYTHON_VERSION, 'ASGI implementation requires Python3.5+')\nclass ReporterMiddlewareTest(BaseTest):\n    default_settings = copy.deepcopy(rollbar.SETTINGS)\n\n    def setUp(self):\n        importlib.reload(rollbar)\n        rollbar.SETTINGS = copy.deepcopy(self.default_settings)\n        rollbar.SETTINGS['handler'] = 'async'\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_catch_and_report_errors(self, mock_report):\n        from rollbar.contrib.asgi.middleware import ReporterMiddleware\n        from rollbar.lib._async import FailingTestASGIApp, run\n\n        testapp = ReporterMiddleware(FailingTestASGIApp())\n\n        with self.assertRaises(RuntimeError):\n            run(testapp({'type': 'http', 'headers': []}, None, None))\n\n        self.assertTrue(mock_report.called)\n\n        args, kwargs = mock_report.call_args\n        self.assertEqual(kwargs, {})\n\n        exc_type, exc_value, exc_tb = args[0]\n\n        self.assertEqual(exc_type, RuntimeError)\n        self.assertIsInstance(exc_value, RuntimeError)\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar.send_payload')\n    def test_should_add_framework_name_to_payload(self, mock_send_payload, *mocks):\n        import rollbar\n        from rollbar.contrib.asgi.middleware import ReporterMiddleware\n\n        self.assertIsNone(rollbar.BASE_DATA_HOOK)\n\n        ReporterMiddleware(None)  # invoke integration\n        rollbar.report_exc_info()\n\n        self.assertTrue(mock_send_payload.called)\n        payload = mock_send_payload.call_args[0][0]\n\n        self.assertIn('asgi', payload['data']['framework'])\n\n    @unittest.skipUnless(ASYNC_REPORT_ENABLED, 'Requires Python 3.6+')\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_default_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        import rollbar\n        from rollbar.contrib.asgi.middleware import ReporterMiddleware\n        from rollbar.lib._async import FailingTestASGIApp, run\n\n        rollbar.SETTINGS['handler'] = 'default'\n        testapp = ReporterMiddleware(FailingTestASGIApp())\n\n        with self.assertRaises(RuntimeError):\n            run(testapp({'type': 'http', 'headers': []}, None, None))\n\n        self.assertTrue(async_report_exc_info.called)\n        self.assertFalse(sync_report_exc_info.called)\n\n    @unittest.skipUnless(ASYNC_REPORT_ENABLED, 'Requires Python 3.6+')\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_any_async_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        import rollbar\n        from rollbar.contrib.asgi.middleware import ReporterMiddleware\n        from rollbar.lib._async import FailingTestASGIApp, run\n\n        rollbar.SETTINGS['handler'] = 'httpx'\n        testapp = ReporterMiddleware(FailingTestASGIApp())\n\n        with self.assertRaises(RuntimeError):\n            run(testapp({'type': 'http', 'headers': []}, None, None))\n\n        self.assertTrue(async_report_exc_info.called)\n        self.assertFalse(sync_report_exc_info.called)\n\n    @unittest.skipUnless(ASYNC_REPORT_ENABLED, 'Requires Python 3.6+')\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_sync_report_exc_info_if_non_async_handlers(\n        self, sync_report_exc_info, async_report_exc_info, mock_log\n    ):\n        import rollbar\n        from rollbar.contrib.asgi.middleware import ReporterMiddleware\n        from rollbar.lib._async import FailingTestASGIApp, run\n\n        rollbar.SETTINGS['handler'] = 'threading'\n        testapp = ReporterMiddleware(FailingTestASGIApp())\n\n        with self.assertRaises(RuntimeError):\n            run(testapp({'type': 'http', 'headers': []}, None, None))\n\n        self.assertFalse(async_report_exc_info.called)\n        self.assertTrue(sync_report_exc_info.called)\n        mock_log.assert_called_once_with(\n            'Failed to report asynchronously. Trying to report synchronously.'\n        )\n\n    def test_should_support_http_only(self):\n        from rollbar.contrib.asgi.middleware import ReporterMiddleware\n        from rollbar.lib._async import FailingTestASGIApp, run\n\n        testapp = ReporterMiddleware(FailingTestASGIApp())\n\n        with mock.patch('rollbar.report_exc_info') as mock_report:\n            with self.assertRaises(RuntimeError):\n                run(testapp({'type': 'http', 'headers': []}, None, None))\n\n            self.assertTrue(mock_report.called)\n\n        with mock.patch('rollbar.report_exc_info') as mock_report:\n            with self.assertRaises(RuntimeError):\n                run(testapp({'type': 'websocket'}, None, None))\n\n            self.assertFalse(mock_report.called)\n\n    def test_should_support_type_hints(self):\n        from rollbar.contrib.asgi.types import Receive, Scope, Send\n\n        self.assertDictEqual(\n            rollbar.contrib.asgi.ReporterMiddleware.__call__.__annotations__,\n            {'scope': Scope, 'receive': Receive, 'send': Send, 'return': None},\n        )\n"
  },
  {
    "path": "rollbar/test/asgi_tests/test_spec.py",
    "content": "import inspect\nimport sys\n\nimport unittest\n\nfrom rollbar.test import BaseTest\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 5)\n\n\n@unittest.skipUnless(ALLOWED_PYTHON_VERSION, 'ASGI implementation requires Python3.5+')\nclass ASGISpecTest(BaseTest):\n    def test_asgi_v3_middleware_is_single_callable_coroutine(self):\n        from rollbar.contrib.asgi import ReporterMiddleware\n\n        app = ReporterMiddleware(object)\n\n        self.assertFalse(inspect.isclass(app))\n        self.assertTrue(hasattr(app, '__call__'))\n        self.assertTrue(inspect.iscoroutinefunction(app.__call__))\n\n    def test_asgi_v3_app_signature(self):\n        from rollbar.contrib.asgi import ReporterMiddleware\n\n        app = ReporterMiddleware(object)\n        app_args = inspect.getfullargspec(app).args\n\n        self.assertListEqual(app_args, ['self', 'scope', 'receive', 'send'])\n"
  },
  {
    "path": "rollbar/test/async_tests/__init__.py",
    "content": "import sys\nimport unittest\n\n\ndef _load_tests(loader, tests, pattern):\n    return unittest.TestSuite()\n\n\nif sys.version_info < (3, 6):\n    load_tests = _load_tests\n"
  },
  {
    "path": "rollbar/test/async_tests/test_async.py",
    "content": "import copy\nimport sys\n\nfrom unittest import mock\n\nimport unittest\n\nimport rollbar\nfrom rollbar.lib._async import AsyncMock\nfrom rollbar.test import BaseTest\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(ALLOWED_PYTHON_VERSION, 'Async support requires Python3.6+')\nclass AsyncLibTest(BaseTest):\n    default_settings = copy.deepcopy(rollbar.SETTINGS)\n\n    def setUp(self):\n        self.access_token = 'aaaabbbbccccddddeeeeffff00001111'\n        rollbar.SETTINGS = copy.deepcopy(self.default_settings)\n        rollbar._initialized = False\n        rollbar.init(self.access_token, handler='async')\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_exception(self, send_payload):\n        from rollbar.lib._async import report_exc_info, run\n\n        def _raise():\n            try:\n                raise Exception('foo')\n            except:\n                return run(report_exc_info())\n\n        uuid = _raise()\n\n        send_payload.assert_called_once()\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['data']['uuid'], uuid)\n        self.assertEqual(payload['access_token'], self.access_token)\n        self.assertIn('body', payload['data'])\n        self.assertIn('trace', payload['data']['body'])\n        self.assertNotIn('trace_chain', payload['data']['body'])\n        self.assertIn('exception', payload['data']['body']['trace'])\n        self.assertEqual(\n            payload['data']['body']['trace']['exception']['message'], 'foo'\n        )\n        self.assertEqual(\n            payload['data']['body']['trace']['exception']['class'], 'Exception'\n        )\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('locals', payload['data']['body']['trace']['frames'][-1])\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_messsage(self, send_payload):\n        from rollbar.lib._async import report_message, run\n\n        uuid = run(report_message('foo'))\n\n        send_payload.assert_called_once()\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['data']['uuid'], uuid)\n        self.assertEqual(payload['access_token'], self.access_token)\n        self.assertIn('body', payload['data'])\n        self.assertIn('message', payload['data']['body'])\n        self.assertIn('body', payload['data']['body']['message'])\n        self.assertEqual(payload['data']['body']['message']['body'], 'foo')\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_run_rollbar_report_exc_info(self, rollbar_report_exc_info):\n        from rollbar.lib._async import report_exc_info, run\n\n        try:\n            raise Exception()\n        except Exception:\n            run(\n                report_exc_info(\n                    'exc_info',\n                    'request',\n                    'extra_data',\n                    'payload_data',\n                    'level_data',\n                    foo='bar',\n                )\n            )\n\n        rollbar_report_exc_info.assert_called_once_with(\n            'exc_info', 'request', 'extra_data', 'payload_data', 'level_data', foo='bar'\n        )\n\n    @mock.patch('rollbar.report_message')\n    def test_should_run_rollbar_report_message(self, rollbar_report_message):\n        from rollbar.lib._async import report_message, run\n\n        run(report_message('message', 'level', 'request', 'extra_data', 'payload_data'))\n\n        rollbar_report_message.assert_called_once_with(\n            'message', 'level', 'request', 'extra_data', 'payload_data'\n        )\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_exc_info_should_use_async_handler_regardless_of_settings(\n        self, mock__send_payload_async, mock_log\n    ):\n        import rollbar\n        from rollbar.lib._async import report_exc_info, run\n\n        rollbar.SETTINGS['handler'] = 'thread'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n\n        run(report_exc_info())\n\n        self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n        mock__send_payload_async.assert_called_once()\n        mock_log.assert_called_once_with(\n            'Running coroutines requires async compatible handler. Switching to default async handler.'\n        )\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_message_should_use_async_handler_regardless_of_settings(\n        self, mock__send_payload_async, mock_log\n    ):\n        import rollbar\n        from rollbar.lib._async import report_message, run\n\n        rollbar.SETTINGS['handler'] = 'thread'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n\n        run(report_message('foo'))\n\n        self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n        mock__send_payload_async.assert_called_once()\n        mock_log.assert_called_once_with(\n            'Running coroutines requires async compatible handler. Switching to default async handler.'\n        )\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_exc_info_should_allow_async_handler(\n        self, mock__send_payload_async, mock_log\n    ):\n        import rollbar\n        from rollbar.lib._async import report_exc_info, run\n\n        rollbar.SETTINGS['handler'] = 'async'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'async')\n\n        run(report_exc_info())\n\n        self.assertEqual(rollbar.SETTINGS['handler'], 'async')\n        mock__send_payload_async.assert_called_once()\n        mock_log.assert_not_called()\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_message_should_allow_async_handler(\n        self, mock__send_payload_async, mock_log\n    ):\n        import rollbar\n        from rollbar.lib._async import report_message, run\n\n        rollbar.SETTINGS['handler'] = 'async'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'async')\n\n        run(report_message('foo'))\n\n        self.assertEqual(rollbar.SETTINGS['handler'], 'async')\n        mock__send_payload_async.assert_called_once()\n        mock_log.assert_not_called()\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_httpx')\n    def test_report_exc_info_should_allow_httpx_handler(\n        self, mock__send_payload_httpx, mock_log\n    ):\n        import rollbar\n        from rollbar.lib._async import report_exc_info, run\n\n        rollbar.SETTINGS['handler'] = 'httpx'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'httpx')\n\n        run(report_exc_info())\n\n        self.assertEqual(rollbar.SETTINGS['handler'], 'httpx')\n        mock__send_payload_httpx.assert_called_once()\n        mock_log.assert_not_called()\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_httpx')\n    def test_report_message_should_allow_httpx_handler(\n        self, mock__send_payload_httpx, mock_log\n    ):\n        import rollbar\n        from rollbar.lib._async import report_message, run\n\n        rollbar.SETTINGS['handler'] = 'httpx'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'httpx')\n\n        run(report_message('foo', 'error'))\n\n        self.assertEqual(rollbar.SETTINGS['handler'], 'httpx')\n        mock__send_payload_httpx.assert_called_once()\n        mock_log.assert_not_called()\n\n    @mock.patch('logging.Logger.warning')\n    def test_ctx_manager_should_use_async_handler(self, mock_log):\n        import rollbar\n        from rollbar.lib._async import AsyncHandler\n\n        rollbar.SETTINGS['handler'] = 'thread'\n        with AsyncHandler() as handler:\n            self.assertEqual(handler, 'async')\n            mock_log.assert_called_once_with(\n                'Running coroutines requires async compatible handler.'\n                ' Switching to default async handler.'\n            )\n        rollbar.SETTINGS['handler'] = 'thread'\n\n    @mock.patch('logging.Logger.warning')\n    def test_ctx_manager_should_use_global_handler_if_contextvar_is_not_supported(\n        self, mock_log\n    ):\n        import rollbar\n        import rollbar.lib._async\n        from rollbar.lib._async import AsyncHandler\n\n        try:\n            # simulate missing `contextvars` module\n            _ctx_handler = rollbar.lib._async._ctx_handler\n            rollbar.lib._async._ctx_handler = None\n\n            rollbar.SETTINGS['handler'] = 'thread'\n            self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n\n            with AsyncHandler() as handler:\n                self.assertEqual(handler, 'thread')\n                mock_log.assert_not_called()\n\n            self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n        finally:\n            # restore original _ctx_handler\n            rollbar.lib._async._ctx_handler = _ctx_handler\n\n    @mock.patch('logging.Logger.warning')\n    def test_ctx_manager_should_not_substitute_global_handler(self, mock_log):\n        import rollbar\n        from rollbar.lib._async import AsyncHandler\n\n        rollbar.SETTINGS['handler'] = 'thread'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n\n        with AsyncHandler() as handler:\n            self.assertEqual(handler, 'async')\n            self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n\n        self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n        mock_log.assert_called_once_with(\n            'Running coroutines requires async compatible handler.'\n            ' Switching to default async handler.'\n        )\n\n    @mock.patch('rollbar._send_payload_httpx')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_exc_info_message_should_allow_multiple_async_handlers(\n        self, mock__send_payload_async, mock__send_payload_httpx\n    ):\n        import asyncio\n        import rollbar\n        from rollbar.lib._async import report_exc_info, run\n\n        async def report(handler):\n            rollbar.SETTINGS['handler'] = handler\n            try:\n                raise Exception('foo')\n            except:\n                await report_exc_info()\n\n        async def send_reports():\n            await asyncio.gather(report('async'), report('httpx'))\n\n        run(send_reports())\n\n        mock__send_payload_async.assert_called_once()\n        mock__send_payload_httpx.assert_called_once()\n\n    @mock.patch('rollbar._send_payload_httpx')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_message_should_allow_multiple_async_handlers(\n        self, mock__send_payload_async, mock__send_payload_httpx\n    ):\n        import asyncio\n        import rollbar\n        from rollbar.lib._async import report_message, run\n\n        async def report(handler):\n            rollbar.SETTINGS['handler'] = handler\n            await report_message('foo')\n\n        async def send_reports():\n            await asyncio.gather(report('async'), report('httpx'))\n\n        run(send_reports())\n\n        mock__send_payload_async.assert_called_once()\n        mock__send_payload_httpx.assert_called_once()\n\n    @mock.patch('rollbar._send_payload')\n    @mock.patch('rollbar._send_payload_thread')\n    @mock.patch('rollbar._send_payload_httpx')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_exc_info_should_allow_multiple_handlers(\n        self,\n        mock__send_payload_async,\n        mock__send_payload_httpx,\n        mock__send_payload_thread,\n        mock__send_payload,\n    ):\n        import asyncio\n        import rollbar\n        from rollbar.lib._async import report_exc_info, run\n\n        async def async_report(handler):\n            rollbar.SETTINGS['handler'] = handler\n            try:\n                raise Exception('foo')\n            except:\n                await report_exc_info()\n\n        async def sync_report(handler):\n            rollbar.SETTINGS['handler'] = handler\n            try:\n                raise Exception('foo')\n            except:\n                rollbar.report_exc_info()\n\n        async def send_reports():\n            await asyncio.gather(\n                sync_report('thread'),\n                async_report('httpx'),\n                sync_report('blocking'),\n                async_report('async'),\n            )\n\n        run(send_reports())\n\n        mock__send_payload_async.assert_called_once()\n        mock__send_payload_httpx.assert_called_once()\n        mock__send_payload_thread.assert_called_once()\n        mock__send_payload.assert_called_once()\n\n    @mock.patch('rollbar._send_payload')\n    @mock.patch('rollbar._send_payload_thread')\n    @mock.patch('rollbar._send_payload_httpx')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_message_should_allow_multiple_handlers(\n        self,\n        mock__send_payload_async,\n        mock__send_payload_httpx,\n        mock__send_payload_thread,\n        mock__send_payload,\n    ):\n        import asyncio\n        import rollbar\n        from rollbar.lib._async import report_message, run\n\n        async def async_report(handler):\n            rollbar.SETTINGS['handler'] = handler\n            await report_message('foo')\n\n        async def sync_report(handler):\n            rollbar.SETTINGS['handler'] = handler\n            rollbar.report_message('foo')\n\n        async def send_reports():\n            await asyncio.gather(\n                sync_report('thread'),\n                async_report('httpx'),\n                sync_report('blocking'),\n                async_report('async'),\n            )\n\n        run(send_reports())\n\n        mock__send_payload_async.assert_called_once()\n        mock__send_payload_httpx.assert_called_once()\n        mock__send_payload_thread.assert_called_once()\n        mock__send_payload.assert_called_once()\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_thread')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_exc_info_should_allow_multiple_handlers_with_threads(\n        self,\n        mock__send_payload_async,\n        mock__send_payload_thread,\n        mock_log,\n    ):\n        import time\n        import threading\n        import rollbar\n        from rollbar.lib._async import report_exc_info, run\n\n        async def async_report():\n            try:\n                raise Exception('foo')\n            except:\n                await report_exc_info()\n\n        def sync_report():\n            # give a chance to execute async_report() first\n            time.sleep(0.1)\n\n            try:\n                raise Exception('foo')\n            except:\n                rollbar.report_exc_info()\n\n        rollbar.SETTINGS['handler'] = 'thread'\n        t1 = threading.Thread(target=run, args=(async_report(),))\n        t2 = threading.Thread(target=sync_report)\n\n        t1.start()\n        t2.start()\n        t1.join()\n        t2.join()\n\n        mock__send_payload_async.assert_called_once()\n        mock__send_payload_thread.assert_called_once()\n        mock_log.assert_called_once_with(\n            'Running coroutines requires async compatible handler. Switching to default async handler.'\n        )\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar._send_payload_thread')\n    @mock.patch('rollbar._send_payload_async')\n    def test_report_message_should_allow_multiple_handlers_with_threads(\n        self,\n        mock__send_payload_async,\n        mock__send_payload_thread,\n        mock_log,\n    ):\n        import time\n        import threading\n        import rollbar\n        from rollbar.lib._async import report_message, run\n\n        async def async_report():\n            await report_message('foo')\n\n        def sync_report():\n            # give a chance to execute async_report() first\n            time.sleep(0.1)\n            rollbar.report_message('foo')\n\n        rollbar.SETTINGS['handler'] = 'thread'\n        t1 = threading.Thread(target=run, args=(async_report(),))\n        t2 = threading.Thread(target=sync_report)\n\n        t1.start()\n        t2.start()\n        t1.join()\n        t2.join()\n\n        mock__send_payload_async.assert_called_once()\n        mock__send_payload_thread.assert_called_once()\n        mock_log.assert_called_once_with(\n            'Running coroutines requires async compatible handler. Switching to default async handler.'\n        )\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    def test_should_try_report_with_async_handler(self, async_report_exc_info):\n        import rollbar\n        from rollbar.lib._async import run, try_report\n\n        rollbar.SETTINGS['handler'] = 'async'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'async')\n\n        run(try_report())\n\n        async_report_exc_info.assert_called_once()\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    def test_should_try_report_if_default_handler(self, async_report_exc_info):\n        import rollbar\n        from rollbar.lib._async import run, try_report\n\n        rollbar.SETTINGS['handler'] = 'default'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'default')\n\n        run(try_report())\n\n        async_report_exc_info.assert_called_once()\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    def test_should_not_try_report_with_async_handler_if_non_async_handler(\n        self, async_report_exc_info\n    ):\n        import rollbar\n        from rollbar.lib._async import RollbarAsyncError, run, try_report\n\n        rollbar.SETTINGS['handler'] = 'thread'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'thread')\n\n        with self.assertRaises(RollbarAsyncError):\n            run(try_report())\n\n        async_report_exc_info.assert_not_called()\n\n    @mock.patch('rollbar.lib._async.httpx', None)\n    def test_try_report_should_raise_exc_if_httpx_package_is_missing(self):\n        import rollbar\n        from rollbar.lib._async import RollbarAsyncError, run, try_report\n\n        rollbar.SETTINGS['handler'] = 'httpx'\n        self.assertEqual(rollbar.SETTINGS['handler'], 'httpx')\n\n        with self.assertRaises(RollbarAsyncError):\n            run(try_report())\n\n    @mock.patch('asyncio.ensure_future')\n    def test_should_schedule_task_in_event_loop(self, ensure_future):\n        from rollbar.lib._async import call_later, coroutine\n\n        try:\n            if sys.version_info >= (3, 7):\n                with mock.patch('asyncio.create_task') as create_task:\n                    coro = coroutine()\n                    call_later(coro)\n\n                    create_task.assert_called_once_with(coro)\n                    ensure_future.assert_not_called()\n            else:\n                coro = coroutine()\n                call_later(coro)\n\n                ensure_future.assert_called_once_with(coro)\n        finally:\n            # make sure the coroutine is closed to avoid RuntimeWarning by calling\n            # coroutine without awaiting it later\n            coro.close()\n"
  },
  {
    "path": "rollbar/test/fastapi_tests/__init__.py",
    "content": "import sys\nimport unittest\n\n\ndef _load_tests(loader, tests, pattern):\n    return unittest.TestSuite()\n\n\nif sys.version_info < (3, 6):\n    load_tests = _load_tests\n"
  },
  {
    "path": "rollbar/test/fastapi_tests/test_logger.py",
    "content": "import importlib\nimport sys\n\nfrom unittest import mock\n\ntry:\n    import fastapi\n\n    FASTAPI_INSTALLED = True\nexcept ImportError:\n    FASTAPI_INSTALLED = False\n\nimport unittest\n\nimport rollbar\nfrom rollbar.test import BaseTest\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(\n    FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION,\n    'FastAPI LoggerMiddleware requires Python3.6+',\n)\nclass LoggerMiddlewareTest(BaseTest):\n    def setUp(self):\n        importlib.reload(rollbar)\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar.send_payload')\n    def test_should_add_framework_version_to_payload(self, mock_send_payload, *mocks):\n        import fastapi\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi.logger import LoggerMiddleware\n\n        self.assertIsNone(rollbar.BASE_DATA_HOOK)\n\n        app = FastAPI()\n        app.add_middleware(LoggerMiddleware)\n        app.build_middleware_stack()\n\n        rollbar.report_exc_info()\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n\n        self.assertIn('fastapi', payload['data']['framework'])\n        self.assertIn(fastapi.__version__, payload['data']['framework'])\n\n    def test_should_support_type_hints(self):\n        from starlette.types import Receive, Scope, Send\n        import rollbar.contrib.fastapi.logger\n\n        self.assertDictEqual(\n            rollbar.contrib.fastapi.logger.LoggerMiddleware.__call__.__annotations__,\n            {'scope': Scope, 'receive': Receive, 'send': Send, 'return': None},\n        )\n\n    @mock.patch('rollbar.contrib.starlette.logger.store_current_request')\n    def test_should_store_current_request(self, store_current_request):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.logger import LoggerMiddleware\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        expected_scope = {\n            'client': ['testclient', 50000],\n            'headers': [\n                (b'host', b'testserver'),\n                (b'accept', b'*/*'),\n                (b'accept-encoding', b'gzip, deflate'),\n                (b'connection', b'keep-alive'),\n                (b'user-agent', b'testclient'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/',\n            'query_string': b'',\n            'root_path': '',\n            'scheme': 'http',\n            'server': ['testserver', 80],\n            'type': 'http',\n        }\n\n        app = FastAPI()\n        app.add_middleware(LoggerMiddleware)\n\n        @app.get('/')\n        async def read_root():\n            return 'ok'\n\n        client = TestClient(app)\n        client.get('/')\n\n        store_current_request.assert_called_once()\n\n        scope = store_current_request.call_args[0][0]\n        self.assertEqual(scope, {**expected_scope, **scope})\n\n    def test_should_return_current_request(self):\n        from fastapi import FastAPI\n        from starlette.requests import Request\n        from rollbar.contrib.fastapi.logger import LoggerMiddleware\n        from rollbar.contrib.fastapi import get_current_request\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        app.add_middleware(LoggerMiddleware)\n\n        @app.get('/')\n        async def read_root():\n            request = get_current_request()\n\n            self.assertIsNotNone(request)\n            self.assertIsInstance(request, Request)\n\n        client = TestClient(app)\n        client.get('/')\n\n    @mock.patch('rollbar.contrib.starlette.requests.ContextVar', None)\n    @mock.patch('logging.Logger.error')\n    def test_should_not_return_current_request_for_older_python(self, mock_log):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.logger import LoggerMiddleware\n        from rollbar.contrib.fastapi import get_current_request\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        app.add_middleware(LoggerMiddleware)\n\n        @app.get('/')\n        async def read_root():\n            self.assertIsNone(get_current_request())\n            mock_log.assert_called_once_with(\n                'Python 3.7+ (or aiocontextvars package)'\n                ' is required to receive current request.'\n            )\n\n        client = TestClient(app)\n        client.get('/')\n"
  },
  {
    "path": "rollbar/test/fastapi_tests/test_middleware.py",
    "content": "import copy\nimport importlib\nimport sys\n\nfrom unittest import mock\n\ntry:\n    import fastapi\n\n    FASTAPI_INSTALLED = True\nexcept ImportError:\n    FASTAPI_INSTALLED = False\n\nimport unittest\n\nimport rollbar\nfrom rollbar.lib._async import AsyncMock\nfrom rollbar.test import BaseTest\nfrom rollbar.test.utils import get_public_attrs\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(\n    FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION, 'FastAPI requires Python3.6+'\n)\nclass ReporterMiddlewareTest(BaseTest):\n    default_settings = copy.deepcopy(rollbar.SETTINGS)\n\n    def setUp(self):\n        importlib.reload(rollbar)\n        rollbar.SETTINGS = copy.deepcopy(self.default_settings)\n        rollbar.SETTINGS['handler'] = 'async'\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_catch_and_report_errors(self, mock_report):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        async def read_root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        mock_report.assert_called_once()\n\n        args, kwargs = mock_report.call_args\n        self.assertEqual(kwargs, {})\n\n        exc_type, exc_value, exc_tb = args[0]\n\n        self.assertEqual(exc_type, ZeroDivisionError)\n        self.assertIsInstance(exc_value, ZeroDivisionError)\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_report_with_request_data(self, mock_report):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n\n        try:\n            from fastapi import Request\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.requests import Request\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        def read_root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        mock_report.assert_called_once()\n        request = mock_report.call_args[0][1]\n\n        self.assertIsInstance(request, Request)\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar._serialize_frame_data')\n    @mock.patch('rollbar.send_payload')\n    def test_should_send_payload_with_request_data(self, mock_send_payload, *mocks):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n\n        try:\n            from fastapi import Request\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.requests import Request\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/{path}')\n        def read_root(path):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/test?param1=value1&param2=value2')\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n        payload_request = payload['data']['request']\n\n        self.assertEqual(payload_request['method'], 'GET')\n        self.assertEqual(payload_request['user_ip'], 'testclient')\n        self.assertEqual(\n            payload_request['url'],\n            'http://testserver/test?param1=value1&param2=value2',\n        )\n        self.assertDictEqual(payload_request['params'], {'path': 'test'})\n        self.assertDictEqual(\n            payload_request['GET'], {'param1': 'value1', 'param2': 'value2'}\n        )\n        self.assertDictEqual(\n            payload_request['headers'],\n            {\n                'accept': '*/*',\n                'accept-encoding': 'gzip, deflate',\n                'connection': 'keep-alive',\n                'host': 'testserver',\n                'user-agent': 'testclient',\n            },\n        )\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar.send_payload')\n    def test_should_add_framework_version_to_payload(self, mock_send_payload, *mocks):\n        import fastapi\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n\n        self.assertIsNone(rollbar.BASE_DATA_HOOK)\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n        app.build_middleware_stack()\n\n        rollbar.report_exc_info()\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n\n        self.assertIn('fastapi', payload['data']['framework'])\n        self.assertIn(fastapi.__version__, payload['data']['framework'])\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_default_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi import ReporterMiddleware\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        rollbar.SETTINGS['handler'] = 'default'\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        async def root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        async_report_exc_info.assert_called_once()\n        sync_report_exc_info.assert_not_called()\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_any_async_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi import ReporterMiddleware\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        rollbar.SETTINGS['handler'] = 'httpx'\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        async def root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        async_report_exc_info.assert_called_once()\n        sync_report_exc_info.assert_not_called()\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_sync_report_exc_info_if_non_async_handlers(\n        self, sync_report_exc_info, async_report_exc_info, mock_log\n    ):\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi import ReporterMiddleware\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        rollbar.SETTINGS['handler'] = 'threading'\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        async def root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        sync_report_exc_info.assert_called_once()\n        async_report_exc_info.assert_not_called()\n        mock_log.assert_called_once_with(\n            'Failed to report asynchronously. Trying to report synchronously.'\n        )\n\n    @unittest.skipUnless(\n        sys.version_info >= (3, 6), 'Global request access requires Python 3.6+'\n    )\n    @mock.patch('rollbar.contrib.starlette.middleware.store_current_request')\n    def test_should_store_current_request(self, store_current_request):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        expected_scope = {\n            'client': ['testclient', 50000],\n            'headers': [\n                (b'host', b'testserver'),\n                (b'accept', b'*/*'),\n                (b'accept-encoding', b'gzip, deflate'),\n                (b'connection', b'keep-alive'),\n                (b'user-agent', b'testclient'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/',\n            'query_string': b'',\n            'root_path': '',\n            'scheme': 'http',\n            'server': ['testserver', 80],\n            'type': 'http',\n        }\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        async def read_root():\n            ...\n\n        client = TestClient(app)\n        client.get('/')\n\n        store_current_request.assert_called_once()\n\n        scope = store_current_request.call_args[0][0]\n        self.assertEqual(scope, {**expected_scope, **scope})\n\n    @unittest.skipUnless(\n        sys.version_info >= (3, 6), 'Global request access is supported in Python 3.6+'\n    )\n    def test_should_return_current_request(self):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n        from rollbar.contrib.fastapi import get_current_request\n\n        try:\n            from fastapi import Request\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.requests import Request\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        async def read_root(original_request: Request):\n            request = get_current_request()\n\n            self.assertEqual(get_public_attrs(request), get_public_attrs(original_request))\n\n        client = TestClient(app)\n        client.get('/')\n\n    @mock.patch('rollbar.contrib.starlette.requests.ContextVar', None)\n    @mock.patch('logging.Logger.error')\n    def test_should_not_return_current_request_for_older_python(self, mock_log):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n        from rollbar.contrib.fastapi import get_current_request\n\n        try:\n            from fastapi import Request\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.requests import Request\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/')\n        async def read_root(original_request: Request):\n            request = get_current_request()\n\n            self.assertIsNone(request)\n            self.assertNotEqual(request, original_request)\n            mock_log.assert_called_once_with(\n                'Python 3.7+ (or aiocontextvars package)'\n                ' is required to receive current request.'\n            )\n\n        client = TestClient(app)\n        client.get('/')\n\n    def test_should_support_http_only(self):\n        from rollbar.contrib.fastapi.middleware import ReporterMiddleware\n        from rollbar.lib._async import FailingTestASGIApp, run\n\n        testapp = ReporterMiddleware(FailingTestASGIApp())\n\n        with mock.patch('rollbar.report_exc_info') as mock_report:\n            with self.assertRaises(RuntimeError):\n                run(testapp({'type': 'http', 'headers': []}, None, None))\n\n            mock_report.assert_called_once()\n\n        with mock.patch('rollbar.report_exc_info') as mock_report:\n            with self.assertRaises(RuntimeError):\n                run(testapp({'type': 'websocket'}, None, None))\n\n            mock_report.assert_not_called()\n\n    def test_should_support_type_hints(self):\n        from starlette.types import Receive, Scope, Send\n        import rollbar.contrib.fastapi.middleware\n\n        self.assertDictEqual(\n            rollbar.contrib.fastapi.middleware.ReporterMiddleware.__call__.__annotations__,\n            {'scope': Scope, 'receive': Receive, 'send': Send, 'return': None},\n        )\n"
  },
  {
    "path": "rollbar/test/fastapi_tests/test_routing.py",
    "content": "import copy\nimport importlib\nimport json\nimport sys\n\nfrom unittest import mock\n\ntry:\n    import fastapi\n\n    FASTAPI_INSTALLED = True\n    ALLOWED_FASTAPI_VERSION = fastapi.__version__ >= '0.41.0'\nexcept ImportError:\n    FASTAPI_INSTALLED = False\n    ALLOWED_FASTAPI_VERSION = False\n\nimport unittest\n\nimport rollbar\nfrom rollbar.lib._async import AsyncMock\nfrom rollbar.test import BaseTest\n\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(\n    FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION, 'FastAPI requires Python3.6+'\n)\nclass LoggingRouteUnsupportedFastAPIVersionTest(BaseTest):\n    def test_should_disable_loading_route_handler_if_fastapi_is_too_old(self):\n        import logging\n        import fastapi\n        from fastapi import FastAPI\n        from fastapi.routing import APIRoute\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi.utils import FastAPIVersionError\n\n        logging.disable(logging.ERROR)  # silent logger for tests\n        fastapi_version = fastapi.__version__\n\n        app = FastAPI()\n        old_route_class = app.router.route_class\n        self.assertEqual(old_route_class, APIRoute)\n\n        fastapi.__version__ = '0'\n        with self.assertRaises(FastAPIVersionError):\n            rollbar_add_to(app)\n\n        fastapi.__version__ = '0.30.3'\n        with self.assertRaises(FastAPIVersionError):\n            rollbar_add_to(app)\n\n        fastapi.__version__ = '0.40.10'\n        with self.assertRaises(FastAPIVersionError):\n            rollbar_add_to(app)\n\n        self.assertEqual(app.router.route_class, old_route_class)\n\n        logging.disable(logging.NOTSET)  # make sure logger is re-enabled\n        fastapi.__version__ = fastapi_version\n\n\n@unittest.skipUnless(\n    FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION, 'FastAPI requires Python3.6+'\n)\n@unittest.skipUnless(ALLOWED_FASTAPI_VERSION, 'FastAPI v0.41.0+ is required')\nclass LoggingRouteTest(BaseTest):\n    default_settings = copy.deepcopy(rollbar.SETTINGS)\n\n    def setUp(self):\n        importlib.reload(rollbar)\n        rollbar.SETTINGS = copy.deepcopy(self.default_settings)\n        rollbar.SETTINGS['handler'] = 'async'\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_catch_and_report_errors(self, mock_report):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        async def read_root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        mock_report.assert_called_once()\n\n        args, kwargs = mock_report.call_args\n        self.assertEqual(kwargs, {})\n\n        exc_type, exc_value, exc_tb = args[0]\n\n        self.assertEqual(exc_type, ZeroDivisionError)\n        self.assertIsInstance(exc_value, ZeroDivisionError)\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_report_with_request_data(self, mock_report):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi import Request\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.requests import Request\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        def read_root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        mock_report.assert_called_once()\n        request = mock_report.call_args[0][1]\n\n        self.assertIsInstance(request, Request)\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar._serialize_frame_data')\n    @mock.patch('rollbar.send_payload')\n    def test_should_send_payload_with_request_data(self, mock_send_payload, *mocks):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/{path}')\n        def read_root(path):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/test?param1=value1&param2=value2')\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n        payload_request = payload['data']['request']\n\n        self.assertEqual(payload_request['method'], 'GET')\n        self.assertEqual(payload_request['user_ip'], 'testclient')\n        self.assertEqual(\n            payload_request['url'],\n            'http://testserver/test?param1=value1&param2=value2',\n        )\n        self.assertDictEqual(payload_request['params'], {'path': 'test'})\n        self.assertDictEqual(\n            payload_request['GET'], {'param1': 'value1', 'param2': 'value2'}\n        )\n        self.assertDictEqual(\n            payload_request['headers'],\n            {\n                'accept': '*/*',\n                'accept-encoding': 'gzip, deflate',\n                'connection': 'keep-alive',\n                'host': 'testserver',\n                'user-agent': 'testclient',\n            },\n        )\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar._serialize_frame_data')\n    @mock.patch('rollbar.send_payload')\n    def test_should_send_payload_with_request_body(self, mock_send_payload, *mocks):\n        from fastapi import Body, FastAPI\n        from pydantic import BaseModel\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        rollbar.SETTINGS['include_request_body'] = True\n        expected_body = {'param1': 'value1', 'param2': 'value2'}\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        class TestBody(BaseModel):\n            param1: str\n            param2: str\n\n        @app.post('/')\n        def read_root(body: TestBody = Body(...)):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.post('/', json=expected_body)\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n        payload_request = payload['data']['request']\n\n        self.assertEqual(payload_request['method'], 'POST')\n        self.assertEqual(payload_request['user_ip'], 'testclient')\n        self.assertEqual(payload_request['url'], 'http://testserver/')\n        self.assertEqual(payload_request['body'], json.dumps(expected_body))\n        self.assertDictEqual(\n            payload_request['headers'],\n            {\n                'accept': '*/*',\n                'accept-encoding': 'gzip, deflate',\n                'connection': 'keep-alive',\n                'content-length': str(len(json.dumps(expected_body))),\n                'content-type': 'application/json',\n                'host': 'testserver',\n                'user-agent': 'testclient',\n            },\n        )\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar._serialize_frame_data')\n    @mock.patch('rollbar.send_payload')\n    def test_should_send_payload_with_form_data(self, mock_send_payload, *mocks):\n        from fastapi import FastAPI, Form\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        expected_form = {'param1': 'value1', 'param2': 'value2'}\n        expected_body = b'param1=value1&param2=value2'\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.post('/')\n        def read_root(param1: str = Form(...), param2: str = Form(...)):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            r = client.post(\n                '/',\n                data=expected_body,\n                headers={'Content-Type': 'application/x-www-form-urlencoded'},\n            )\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n        payload_request = payload['data']['request']\n\n        self.assertEqual(payload_request['method'], 'POST')\n        self.assertEqual(payload_request['user_ip'], 'testclient')\n        self.assertEqual(payload_request['url'], 'http://testserver/')\n        self.assertDictEqual(payload_request['POST'], expected_form)\n        self.assertDictEqual(\n            payload_request['headers'],\n            {\n                'accept': '*/*',\n                'accept-encoding': 'gzip, deflate',\n                'connection': 'keep-alive',\n                'content-length': str(len(expected_body)),\n                'content-type': 'application/x-www-form-urlencoded',\n                'host': 'testserver',\n                'user-agent': 'testclient',\n            },\n        )\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar.send_payload')\n    def test_should_add_framework_version_to_payload(self, mock_send_payload, *mocks):\n        import fastapi\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        self.assertIsNone(rollbar.BASE_DATA_HOOK)\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        rollbar.report_exc_info()\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n\n        self.assertIn('fastapi', payload['data']['framework'])\n        self.assertIn(fastapi.__version__, payload['data']['framework'])\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_default_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        rollbar.SETTINGS['handler'] = 'default'\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        async def root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        async_report_exc_info.assert_called_once()\n        sync_report_exc_info.assert_not_called()\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_any_async_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        rollbar.SETTINGS['handler'] = 'httpx'\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        async def root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        async_report_exc_info.assert_called_once()\n        sync_report_exc_info.assert_not_called()\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_sync_report_exc_info_if_non_async_handlers(\n        self, sync_report_exc_info, async_report_exc_info, mock_log\n    ):\n        from fastapi import FastAPI\n        import rollbar\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        rollbar.SETTINGS['handler'] = 'threading'\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        async def root():\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        sync_report_exc_info.assert_called_once()\n        async_report_exc_info.assert_not_called()\n        mock_log.assert_called_once_with(\n            'Failed to report asynchronously. Trying to report synchronously.'\n        )\n\n    def test_should_enable_loading_route_handler_if_fastapi_version_is_sufficient(self):\n        from fastapi import FastAPI\n        from fastapi.routing import APIRoute\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi.routing import RollbarLoggingRoute\n\n        self.assertTrue(ALLOWED_FASTAPI_VERSION)\n        app = FastAPI()\n        old_route_class = app.router.route_class\n        self.assertEqual(old_route_class, APIRoute)\n\n        new_route_class = rollbar_add_to(app)\n\n        self.assertNotEqual(new_route_class, old_route_class)\n        self.assertEqual(app.router.route_class, new_route_class)\n        self.assertEqual(app.router.route_class, RollbarLoggingRoute)\n\n    def test_should_enable_loading_route_handler_before_adding_routes_to_app(self):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi.routing import RollbarLoggingRoute\n\n        app = FastAPI()\n        old_route_class = app.router.route_class\n        self.assertEqual(len(app.routes), 4)\n\n        new_route_class = rollbar_add_to(app)\n\n        self.assertNotEqual(new_route_class, old_route_class)\n        self.assertEqual(app.router.route_class, new_route_class)\n        self.assertEqual(len(app.routes), 4)\n\n        @app.get('/')\n        async def read_root():\n            ...\n\n        self.assertEqual(app.router.route_class, new_route_class)\n        self.assertEqual(app.router.route_class, RollbarLoggingRoute)\n        self.assertEqual(len(app.routes), 5)\n\n    @mock.patch('logging.Logger.error')\n    def test_should_disable_loading_route_handler_after_adding_routes_to_app(\n        self, mock_log\n    ):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        app = FastAPI()\n        old_route_class = app.router.route_class\n        self.assertEqual(len(app.routes), 4)\n\n        @app.get('/')\n        async def read_root():\n            ...\n\n        self.assertEqual(len(app.routes), 5)\n\n        new_route_class = rollbar_add_to(app)\n\n        self.assertEqual(len(app.routes), 5)\n        self.assertIsNone(new_route_class)\n        self.assertEqual(app.router.route_class, old_route_class)\n        mock_log.assert_called_once_with(\n            'RollbarLoggingRoute must to be added to a bare router'\n            ' (before adding routes). See docs for more details.'\n        )\n\n    def test_should_enable_loading_route_handler_before_adding_routes_to_router(self):\n        from fastapi import APIRouter, FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi.routing import RollbarLoggingRoute\n\n        app = FastAPI()\n        router = APIRouter()\n\n        old_app_route_class = app.router.route_class\n        old_router_route_class = router.route_class\n\n        self.assertEqual(len(app.routes), 4)\n        self.assertEqual(len(router.routes), 0)\n\n        new_route_class = rollbar_add_to(router)\n\n        self.assertNotEqual(new_route_class, old_router_route_class)\n        self.assertEqual(router.route_class, new_route_class)\n        self.assertEqual(router.route_class, RollbarLoggingRoute)\n        self.assertEqual(app.router.route_class, old_app_route_class)\n        self.assertEqual(len(app.routes), 4)\n        self.assertEqual(len(router.routes), 0)\n\n        @router.get('/')\n        async def read_root():\n            ...\n\n        app.include_router(router)\n\n        self.assertEqual(router.route_class, new_route_class)\n        self.assertEqual(len(app.routes), 5)\n\n    @mock.patch('logging.Logger.error')\n    def test_should_disable_loading_route_handler_after_adding_routes_to_router(\n        self, mock_log\n    ):\n        from fastapi import APIRouter, FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        app = FastAPI()\n        router = APIRouter()\n\n        old_app_route_class = app.router.route_class\n        old_router_route_class = router.route_class\n\n        self.assertEqual(len(app.routes), 4)\n        self.assertEqual(len(router.routes), 0)\n\n        @router.get('/')\n        async def read_root():\n            ...\n\n        app.include_router(router)\n        self.assertEqual(len(app.routes), 5)\n        self.assertEqual(len(router.routes), 1)\n\n        new_route_class = rollbar_add_to(app)\n\n        self.assertEqual(len(app.routes), 5)\n        self.assertEqual(len(router.routes), 1)\n        self.assertIsNone(new_route_class)\n        self.assertEqual(app.router.route_class, old_app_route_class)\n        self.assertEqual(router.route_class, old_router_route_class)\n        mock_log.assert_called_once_with(\n            'RollbarLoggingRoute must to be added to a bare router'\n            ' (before adding routes). See docs for more details.'\n        )\n\n    def test_should_enable_loading_route_handler_for_multiple_routers(self):\n        from fastapi import APIRouter, FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi.routing import RollbarLoggingRoute\n\n        app = FastAPI()\n        router1 = APIRouter()\n        router2 = APIRouter()\n        router3 = APIRouter()\n\n        old_app_route_class = app.router.route_class\n        old_router1_route_class = router1.route_class\n        old_router2_route_class = router2.route_class\n        old_router3_route_class = router3.route_class\n\n        self.assertEqual(len(app.routes), 4)\n        self.assertEqual(len(router1.routes), 0)\n        self.assertEqual(len(router2.routes), 0)\n        self.assertEqual(len(router3.routes), 0)\n\n        new_router1_route_class = rollbar_add_to(router1)\n        new_router2_route_class = rollbar_add_to(router2)\n\n        self.assertNotEqual(new_router1_route_class, old_router1_route_class)\n        self.assertNotEqual(new_router2_route_class, old_router2_route_class)\n        self.assertEqual(router1.route_class, RollbarLoggingRoute)\n        self.assertEqual(router2.route_class, RollbarLoggingRoute)\n        self.assertEqual(router1.route_class, new_router1_route_class)\n        self.assertEqual(router2.route_class, new_router2_route_class)\n        self.assertEqual(router3.route_class, old_router3_route_class)\n        self.assertEqual(app.router.route_class, old_app_route_class)\n        self.assertEqual(len(app.routes), 4)\n        self.assertEqual(len(router1.routes), 0)\n        self.assertEqual(len(router2.routes), 0)\n        self.assertEqual(len(router3.routes), 0)\n\n        @router1.get('/')\n        async def read1():\n            ...\n\n        @router2.get('/')\n        async def read2():\n            ...\n\n        @router3.get('/')\n        async def read3():\n            ...\n\n        app.include_router(router1)\n        app.include_router(router2)\n        app.include_router(router3)\n\n        self.assertEqual(router1.route_class, new_router1_route_class)\n        self.assertEqual(router2.route_class, new_router2_route_class)\n        self.assertEqual(router3.route_class, old_router3_route_class)\n        self.assertEqual(len(app.routes), 7)\n\n    def test_should_enable_loading_route_handler_for_fastapi_app(self):\n        from fastapi import FastAPI\n        from fastapi.routing import APIRoute\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi.routing import RollbarLoggingRoute\n\n        app = FastAPI()\n        old_route_class = app.router.route_class\n        self.assertEqual(old_route_class, APIRoute)\n\n        new_route_class = rollbar_add_to(app)\n\n        self.assertNotEqual(new_route_class, old_route_class)\n        self.assertEqual(app.router.route_class, RollbarLoggingRoute)\n        self.assertEqual(app.router.route_class, new_route_class)\n\n    def test_should_enable_loading_route_handler_for_fastapi_router(self):\n        from fastapi import APIRouter\n        from fastapi.routing import APIRoute\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi.routing import RollbarLoggingRoute\n\n        router = APIRouter()\n        old_route_class = router.route_class\n        self.assertEqual(old_route_class, APIRoute)\n\n        new_route_class = rollbar_add_to(router)\n\n        self.assertNotEqual(new_route_class, old_route_class)\n        self.assertEqual(router.route_class, RollbarLoggingRoute)\n        self.assertEqual(router.route_class, new_route_class)\n\n    @mock.patch('logging.Logger.error')\n    def test_should_disable_loading_route_handler_for_unknown_app(self, mock_log):\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        class UnkownRouter:\n            route_class = None\n\n        class UnknownApp:\n            routes = []\n            router = UnkownRouter()\n\n        app = UnknownApp()\n        old_route_class = app.router.route_class\n\n        new_route_class = rollbar_add_to(app)\n\n        self.assertIsNone(new_route_class)\n        self.assertEqual(app.router.route_class, old_route_class)\n        mock_log.assert_called_once_with(\n            'Error adding RollbarLoggingRoute to application.'\n        )\n\n    @mock.patch('logging.Logger.error')\n    def test_should_disable_loading_route_handler_for_unknown_router(self, mock_log):\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        class UnknownRouter:\n            routes = []\n            route_class = None\n\n        router = UnknownRouter()\n        old_route_class = router.route_class\n\n        new_route_class = rollbar_add_to(router)\n\n        self.assertIsNone(new_route_class)\n        self.assertEqual(router.route_class, old_route_class)\n        mock_log.assert_called_once_with(\n            'Error adding RollbarLoggingRoute to application.'\n        )\n\n    def test_should_warn_if_middleware_in_use(self):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n        from rollbar.contrib.fastapi import ReporterMiddleware as FastAPIMiddleware\n        from rollbar.contrib.starlette import ReporterMiddleware as StarletteMiddleware\n        from rollbar.contrib.asgi import ReporterMiddleware as ASGIMiddleware\n\n        for middleware in (FastAPIMiddleware, StarletteMiddleware, ASGIMiddleware):\n            with mock.patch('logging.Logger.warning') as mock_log:\n                app = FastAPI()\n                app.add_middleware(middleware)\n\n                rollbar_add_to(app)\n\n                mock_log.assert_called_once_with(\n                    f'Detected middleware installed {[middleware]}'\n                    ' while loading Rollbar route handler.'\n                    ' This can cause in duplicate occurrences.'\n                )\n\n    @unittest.skipUnless(\n        sys.version_info >= (3, 6), 'Global request access requires Python 3.6+'\n    )\n    @mock.patch('rollbar.contrib.fastapi.routing.store_current_request')\n    def test_should_store_current_request(self, store_current_request):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.testclient import TestClient\n\n        expected_scope = {\n            'client': ['testclient', 50000],\n            'headers': [\n                (b'host', b'testserver'),\n                (b'user-agent', b'testclient'),\n                (b'accept-encoding', b'gzip, deflate'),\n                (b'accept', b'*/*'),\n                (b'connection', b'keep-alive'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/',\n            'query_string': b'',\n            'root_path': '',\n            'scheme': 'http',\n            'server': ['testserver', 80],\n            'type': 'http',\n        }\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        async def read_root():\n            ...\n\n        client = TestClient(app)\n        client.get('/')\n\n        store_current_request.assert_called_once()\n\n        scope = store_current_request.call_args[0][0]\n        self.assertEqual(scope, {**expected_scope, **scope})\n\n    @unittest.skipUnless(\n        sys.version_info >= (3, 6), 'Global request access is supported in Python 3.6+'\n    )\n    def test_should_return_current_request(self):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi import get_current_request\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi import Request\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.requests import Request\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        async def read_root(original_request: Request):\n            request = get_current_request()\n\n            self.assertEqual(request, original_request)\n\n        client = TestClient(app)\n        client.get('/')\n\n    @mock.patch('rollbar.contrib.starlette.requests.ContextVar', None)\n    @mock.patch('logging.Logger.error')\n    def test_should_not_return_current_request_for_older_python(self, mock_log):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi import get_current_request\n        from rollbar.contrib.fastapi.routing import add_to as rollbar_add_to\n\n        try:\n            from fastapi import Request\n            from fastapi.testclient import TestClient\n        except ImportError:  # Added in FastAPI v0.51.0+\n            from starlette.requests import Request\n            from starlette.testclient import TestClient\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/')\n        async def read_root(original_request: Request):\n            request = get_current_request()\n\n            self.assertIsNone(request)\n            self.assertNotEqual(request, original_request)\n            mock_log.assert_called_once_with(\n                'Python 3.7+ (or aiocontextvars package)'\n                ' is required to receive current request.'\n            )\n\n        client = TestClient(app)\n        client.get('/')\n\n    def test_should_support_type_hints(self):\n        from typing import Optional, Type, Union\n        from fastapi import APIRouter, FastAPI\n        from fastapi.routing import APIRoute\n        import rollbar.contrib.fastapi.routing\n\n        self.assertDictEqual(\n            rollbar.contrib.fastapi.routing.add_to.__annotations__,\n            {\n                'app_or_router': Union[FastAPI, APIRouter],\n                'return': Optional[Type[APIRoute]],\n            },\n        )\n"
  },
  {
    "path": "rollbar/test/fastapi_tests/test_utils.py",
    "content": "import sys\n\ntry:\n    import fastapi\n\n    FASTAPI_INSTALLED = True\nexcept ImportError:\n    FASTAPI_INSTALLED = False\n\nimport unittest\n\nfrom rollbar.test import BaseTest\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(\n    FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION, 'FastAPI requires Python3.6+'\n)\nclass UtilsMiddlewareTest(BaseTest):\n    def test_should_return_installed_rollbar_middlewares(self):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.utils import get_installed_middlewares\n        from rollbar.contrib.fastapi import ReporterMiddleware as FastAPIMiddleware\n        from rollbar.contrib.starlette import ReporterMiddleware as StarletteMiddleware\n        from rollbar.contrib.asgi import ReporterMiddleware as ASGIMiddleware\n\n        # single middleware\n        app = FastAPI()\n        app.add_middleware(FastAPIMiddleware)\n\n        middlewares = get_installed_middlewares(app)\n\n        self.assertListEqual(middlewares, [FastAPIMiddleware])\n\n        # multiple middlewares\n        app = FastAPI()\n        app.add_middleware(FastAPIMiddleware)\n        app.add_middleware(StarletteMiddleware)\n        app.add_middleware(ASGIMiddleware)\n\n        middlewares = get_installed_middlewares(app)\n\n        self.assertListEqual(\n            middlewares, ([ASGIMiddleware, StarletteMiddleware, FastAPIMiddleware])\n        )\n\n    def test_should_return_empty_list_if_rollbar_middlewares_not_installed(self):\n        from fastapi import FastAPI\n        from rollbar.contrib.fastapi.utils import get_installed_middlewares\n        from rollbar.lib._async import BareMiddleware\n\n        # no middlewares\n        app = FastAPI()\n\n        middlewares = get_installed_middlewares(app)\n\n        self.assertListEqual(middlewares, [])\n\n        # no Rollbar middlewares\n        app = FastAPI()\n        app.add_middleware(BareMiddleware)\n\n        middlewares = get_installed_middlewares(app)\n\n        self.assertListEqual(middlewares, [])\n\n\n@unittest.skipUnless(\n    FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION, 'FastAPI requires Python3.6+'\n)\nclass UtilsBareRoutingTest(BaseTest):\n    def test_should_return_true_if_has_bare_routing(self):\n        from fastapi import APIRouter, FastAPI\n        from rollbar.contrib.fastapi.utils import has_bare_routing\n\n        app = FastAPI()\n        self.assertTrue(has_bare_routing(app))\n\n        router = APIRouter()\n        self.assertTrue(has_bare_routing(router))\n\n    def test_should_return_false_if_user_routes_added_to_app(self):\n        from fastapi import APIRouter, FastAPI\n        from rollbar.contrib.fastapi.utils import has_bare_routing\n\n        app = FastAPI()\n        self.assertTrue(has_bare_routing(app))\n\n        @app.get('/')\n        async def read_root():\n            ...\n\n        self.assertFalse(has_bare_routing(app))\n\n    def test_should_return_false_if_user_routes_added_to_router(self):\n        from fastapi import APIRouter\n        from rollbar.contrib.fastapi.utils import has_bare_routing\n\n        router = APIRouter()\n        self.assertTrue(has_bare_routing(router))\n\n        @router.get('/')\n        async def read_root():\n            ...\n\n        self.assertFalse(has_bare_routing(router))\n\n    def test_should_return_false_if_router_added_to_app(self):\n        from fastapi import APIRouter, FastAPI\n        from rollbar.contrib.fastapi.utils import has_bare_routing\n\n        app = FastAPI()\n        router = APIRouter()\n        self.assertTrue(has_bare_routing(app))\n\n        @router.get('/')\n        async def read_root():\n            ...\n\n        app.include_router(router)\n\n        self.assertFalse(has_bare_routing(app))\n\n    def test_should_return_true_if_docs_disabled(self):\n        from fastapi import APIRouter, FastAPI\n        from rollbar.contrib.fastapi.utils import has_bare_routing\n\n        app = FastAPI(docs_url=None, redoc_url=None)\n        self.assertTrue(has_bare_routing(app))\n\n        app = FastAPI(docs_url=None)\n        self.assertTrue(has_bare_routing(app))\n\n        app = FastAPI(redoc_url=None)\n        self.assertTrue(has_bare_routing(app))\n\n        router = APIRouter()\n        self.assertTrue(has_bare_routing(router))\n\n@unittest.skipUnless(\n    FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION, 'FastAPI requires Python3.6+'\n)\nclass UtilsVersionCompareTest(BaseTest):\n    def test_is_current_version_higher_or_equal(self):\n        # Copied from https://semver.org/#spec-item-11\n        versions = [\n            '1.0.0-alpha',\n            '1.0.0-alpha.1',\n            '1.0.0-alpha.beta',\n            '1.0.0-beta',\n            '1.0.0-beta.2',\n            '1.0.0-beta.11',\n            '1.0.0-rc.1',\n            '1.0.0',\n            '1.1.1',\n            '1.100.0-beta2',\n            '1.100.0-beta3',\n        ]\n\n        from rollbar.contrib.fastapi.utils import is_current_version_higher_or_equal\n\n        previous_version = None\n        for version in versions:\n            if previous_version is None:\n                previous_version = version\n                continue\n            with self.subTest(f'{version} >= {previous_version}'):\n                self.assertTrue(is_current_version_higher_or_equal(version, previous_version))\n            previous_version = version\n"
  },
  {
    "path": "rollbar/test/flask_tests/__init__.py",
    "content": ""
  },
  {
    "path": "rollbar/test/flask_tests/test_flask.py",
    "content": "\"\"\"\nTests for Flask instrumentation\n\"\"\"\n\nimport json\nimport sys\nimport os\n\nfrom unittest import mock\n\nimport rollbar\nfrom rollbar.lib.session import reset_current_session\n\nfrom rollbar.test import BaseTest\n\n# access token for https://rollbar.com/rollbar/pyrollbar\nTOKEN = '92c10f5616944b81a2e6f3c6493a0ec2'\n\n# Flask doesn't work on python 3.2, so don't test there.\nALLOWED_PYTHON_VERSION = not (sys.version_info[0] == 3 and sys.version_info[1] == 2)\n\ntry:\n    import flask\n    FLASK_INSTALLED = True\nexcept ImportError:\n    FLASK_INSTALLED = False\n\n\ndef create_app():\n    from flask import Flask, Request, got_request_exception\n    import rollbar.contrib.flask\n    app = Flask(__name__)\n\n    @app.route('/')\n    def index():\n        return 'Index page'\n\n    @app.route('/cause_error', methods=['GET', 'POST'])\n    def cause_error():\n        raise Exception(\"Uh oh\")\n\n    class CustomRequest(Request):\n        @property\n        def rollbar_person(self):\n            return {'id': '123', 'username': 'testuser', 'email': 'test@example.com'}\n\n    app.request_class = CustomRequest\n\n    return app\n\ndef init_rollbar(app):\n    from flask import got_request_exception\n    rollbar._initialized = False\n    rollbar.init(TOKEN, 'flasktest',\n                 root=os.path.dirname(os.path.realpath(__file__)),\n                 allow_logging_basic_config=True,\n                 capture_email=True,\n                 capture_username=True)\n    got_request_exception.connect(rollbar.contrib.flask.report_exception, app)\n\nif ALLOWED_PYTHON_VERSION and FLASK_INSTALLED:\n    class FlaskTest(BaseTest):\n        def setUp(self):\n            super(FlaskTest, self).setUp()\n            self.app = create_app()\n            init_rollbar(self.app)\n            self.client = self.app.test_client()\n            reset_current_session()\n\n        def test_index(self):\n            resp = self.client.get('/')\n            self.assertEqual(resp.status_code, 200)\n\n        def assertStringEqual(self, left, right):\n            if sys.version_info[0] > 2:\n                if hasattr(left, 'decode'):\n                    left = left.decode('ascii')\n                if hasattr(right, 'decode'):\n                    right = right.decode('ascii')\n\n                return self.assertEqual(left, right)\n            else:\n                return self.assertEqual(left, right)\n\n        @mock.patch('rollbar.send_payload')\n        def test_uncaught(self, send_payload):\n            rollbar.SETTINGS['include_request_body'] = True\n            resp = self.client.get('/cause_error?foo=bar',\n                headers={'X-Real-Ip': '1.2.3.4', 'User-Agent': 'Flask Test'})\n            self.assertEqual(resp.status_code, 500)\n\n            self.assertEqual(send_payload.called, True)\n            payload = send_payload.call_args[0][0]\n            data = payload['data']\n\n            self.assertIn('body', data)\n            self.assertEqual(data['body']['trace']['exception']['class'], 'Exception')\n            self.assertStringEqual(data['body']['trace']['exception']['message'], 'Uh oh')\n\n            self.assertIn('person', data)\n            self.assertDictEqual(data['person'],\n                {'id': '123', 'username': 'testuser', 'email': 'test@example.com'})\n\n            self.assertIn('request', data)\n            self.assertEqual(data['request']['url'], 'http://localhost/cause_error?foo=bar')\n\n            # The behavior of implicitly converting werkzeug.ImmutableMultiDict\n            # using dict() changes starting in Python 3.6.\n            # See _build_werkzeug_request_data()\n            if sys.version_info >= (3, 6):\n                self.assertDictEqual(data['request']['GET'], {'foo': 'bar'})\n            else:\n                self.assertDictEqual(data['request']['GET'], {'foo': ['bar']})\n\n            self.assertEqual(data['request']['user_ip'], '1.2.3.4')\n            self.assertEqual(data['request']['method'], 'GET')\n            self.assertEqual(data['request']['headers']['User-Agent'], 'Flask Test')\n\n        @mock.patch('rollbar.send_payload')\n        def test_uncaught_baggage_header(self, send_payload):\n            rollbar.SETTINGS['include_request_body'] = True\n            resp = self.client.get('/cause_error?foo=bar',\n                headers={\n                    'X-Real-Ip': '1.2.3.4',\n                    'User-Agent': 'Flask Test',\n                    'Baggage': 'rollbar.session.id=abcde, rollbar.execution.scope.id = fghij',\n                })\n            self.assertEqual(resp.status_code, 500)\n\n            self.assertEqual(send_payload.called, True)\n            payload = send_payload.call_args[0][0]\n            data = payload['data']\n\n            self.assertEqual(data['attributes'], [\n                {'key': 'session_id', 'value': 'abcde'},\n                {'key': 'execution_scope_id', 'value': 'fghij'},\n            ])\n\n        @mock.patch('rollbar.send_payload')\n        def test_uncaught_json_request(self, send_payload):\n            rollbar.SETTINGS['include_request_body'] = True\n            json_body = {\"hello\": \"world\"}\n            json_body_str = json.dumps(json_body)\n            resp = self.client.post('/cause_error', data=json_body_str,\n                headers={'Content-Type': 'application/json', 'X-Forwarded-For': '5.6.7.8'})\n\n            self.assertEqual(resp.status_code, 500)\n\n            self.assertEqual(send_payload.called, True)\n            payload = send_payload.call_args[0][0]\n            data = payload['data']\n\n            self.assertIn('body', data)\n            self.assertEqual(data['body']['trace']['exception']['class'], 'Exception')\n            self.assertStringEqual(data['body']['trace']['exception']['message'], 'Uh oh')\n\n            self.assertIn('person', data)\n            self.assertDictEqual(data['person'],\n                {'id': '123', 'username': 'testuser', 'email': 'test@example.com'})\n\n            self.assertIn('request', data)\n            self.assertEqual(data['request']['url'], 'http://localhost/cause_error')\n            self.assertEqual(data['request']['body'], json_body)\n            self.assertEqual(data['request']['user_ip'], '5.6.7.8')\n            self.assertEqual(data['request']['method'], 'POST')\n\n        @mock.patch('rollbar.send_payload')\n        def test_uncaught_no_username_no_email(self, send_payload):\n            rollbar.SETTINGS['capture_email'] = False\n            rollbar.SETTINGS['capture_username'] = False\n\n            resp = self.client.get('/cause_error?foo=bar',\n                headers={'X-Real-Ip': '1.2.3.4', 'User-Agent': 'Flask Test'})\n            self.assertEqual(resp.status_code, 500)\n\n            self.assertEqual(send_payload.called, True)\n            payload = send_payload.call_args[0][0]\n            data = payload['data']\n\n            self.assertIn('body', data)\n            self.assertEqual(data['body']['trace']['exception']['class'], 'Exception')\n            self.assertStringEqual(data['body']['trace']['exception']['message'], 'Uh oh')\n\n            self.assertIn('person', data)\n            self.assertDictEqual(data['person'],\n                {'id': '123', 'username': None, 'email': None})\n\n            self.assertIn('request', data)\n            self.assertEqual(data['request']['url'], 'http://localhost/cause_error?foo=bar')\n\n            # The behavior of implicitly converting werkzeug.ImmutableMultiDict\n            # using dict() changes starting in Python 3.6.\n            # See _build_werkzeug_request_data()\n            if sys.version_info >= (3, 6):\n                self.assertDictEqual(data['request']['GET'], {'foo': 'bar'})\n            else:\n                self.assertDictEqual(data['request']['GET'], {'foo': ['bar']})\n\n            self.assertEqual(data['request']['user_ip'], '1.2.3.4')\n            self.assertEqual(data['request']['method'], 'GET')\n            self.assertEqual(data['request']['headers']['User-Agent'], 'Flask Test')\n\n            rollbar.SETTINGS['capture_email'] = True\n            rollbar.SETTINGS['capture_username'] = True\n\n        @mock.patch('rollbar.send_payload')\n        def test_uncaught_no_body(self, send_payload):\n            rollbar.SETTINGS['include_request_body'] = False\n\n            resp = self.client.get('/cause_error?foo=bar',\n                headers={'X-Real-Ip': '1.2.3.4', 'User-Agent': 'Flask Test'})\n            self.assertEqual(resp.status_code, 500)\n\n            self.assertEqual(send_payload.called, True)\n            payload = send_payload.call_args[0][0]\n            data = payload['data']\n\n            self.assertIn('request', data)\n            self.assertNotIn('body', data['request'])\n\n            rollbar.SETTINGS['include_request_body'] = True\n"
  },
  {
    "path": "rollbar/test/starlette_tests/__init__.py",
    "content": "import sys\nimport unittest\n\n\ndef _load_tests(loader, tests, pattern):\n    return unittest.TestSuite()\n\n\nif sys.version_info < (3, 6):\n    load_tests = _load_tests\n"
  },
  {
    "path": "rollbar/test/starlette_tests/test_logger.py",
    "content": "import importlib\nimport sys\n\nfrom unittest import mock\n\ntry:\n    import starlette\n\n    STARLETTE_INSTALLED = True\nexcept ImportError:\n    STARLETTE_INSTALLED = False\n\nimport unittest\n\nimport rollbar\nfrom rollbar.test import BaseTest\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(\n    STARLETTE_INSTALLED and ALLOWED_PYTHON_VERSION,\n    'Starlette LoggerMiddleware requires Python3.6+',\n)\nclass LoggerMiddlewareTest(BaseTest):\n    def setUp(self):\n        importlib.reload(rollbar)\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar.send_payload')\n    def test_should_add_framework_version_to_payload(self, mock_send_payload, *mocks):\n        import starlette\n        from starlette.applications import Starlette\n        import rollbar\n        from rollbar.contrib.starlette.logger import LoggerMiddleware\n\n        self.assertIsNone(rollbar.BASE_DATA_HOOK)\n\n        app = Starlette()\n        app.add_middleware(LoggerMiddleware)\n        app.build_middleware_stack()\n\n        rollbar.report_exc_info()\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n\n        self.assertIn('starlette', payload['data']['framework'])\n        self.assertIn(starlette.__version__, payload['data']['framework'])\n\n    def test_should_support_type_hints(self):\n        from starlette.types import Receive, Scope, Send\n        import rollbar.contrib.starlette.logger\n\n        self.assertDictEqual(\n            rollbar.contrib.starlette.logger.LoggerMiddleware.__call__.__annotations__,\n            {'scope': Scope, 'receive': Receive, 'send': Send, 'return': None},\n        )\n\n    @mock.patch('rollbar.contrib.starlette.logger.store_current_request')\n    def test_should_store_current_request(self, store_current_request):\n        from starlette.applications import Starlette\n        from starlette.responses import PlainTextResponse\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette.logger import LoggerMiddleware\n\n        expected_scope = {\n            'client': ['testclient', 50000],\n            'headers': [\n                (b'host', b'testserver'),\n                (b'accept', b'*/*'),\n                (b'accept-encoding', b'gzip, deflate'),\n                (b'connection', b'keep-alive'),\n                (b'user-agent', b'testclient'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/',\n            'query_string': b'',\n            'root_path': '',\n            'scheme': 'http',\n            'server': ['testserver', 80],\n            'type': 'http',\n        }\n\n        app = Starlette()\n        app.add_middleware(LoggerMiddleware)\n\n        @app.route('/{param}')\n        async def root(request):\n            return PlainTextResponse('OK')\n\n        client = TestClient(app)\n        client.get('/')\n\n        store_current_request.assert_called_once()\n\n        scope = store_current_request.call_args[0][0]\n        self.assertEqual(scope, {**expected_scope, **scope})\n\n    def test_should_return_current_request(self):\n        from starlette.applications import Starlette\n        from starlette.responses import PlainTextResponse\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette import get_current_request\n        from rollbar.contrib.starlette.logger import LoggerMiddleware\n\n        app = Starlette()\n        app.add_middleware(LoggerMiddleware)\n\n        @app.route('/')\n        async def root(request):\n            self.assertIsNotNone(get_current_request())\n\n            return PlainTextResponse('OK')\n\n        client = TestClient(app)\n        client.get('/')\n\n    @mock.patch('rollbar.contrib.starlette.requests.ContextVar', None)\n    @mock.patch('logging.Logger.error')\n    def test_should_not_return_current_request_for_older_python(self, mock_log):\n        from starlette.applications import Starlette\n        from starlette.responses import PlainTextResponse\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette import get_current_request\n        from rollbar.contrib.starlette.logger import LoggerMiddleware\n\n        app = Starlette()\n        app.add_middleware(LoggerMiddleware)\n\n        @app.route('/')\n        async def root(request):\n            self.assertIsNone(get_current_request())\n            mock_log.assert_called_once_with(\n                'Python 3.7+ (or aiocontextvars package) is required to receive current request.'\n            )\n\n            return PlainTextResponse('OK')\n\n        client = TestClient(app)\n        client.get('/')\n"
  },
  {
    "path": "rollbar/test/starlette_tests/test_middleware.py",
    "content": "import copy\nimport importlib\nimport sys\n\nfrom unittest import mock\n\ntry:\n    import starlette\n\n    STARLETTE_INSTALLED = True\nexcept ImportError:\n    STARLETTE_INSTALLED = False\n\nimport unittest\n\nimport rollbar\nfrom rollbar.lib._async import AsyncMock\nfrom rollbar.test import BaseTest\nfrom rollbar.test.utils import get_public_attrs\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(\n    STARLETTE_INSTALLED and ALLOWED_PYTHON_VERSION, 'Starlette requires Python3.6+'\n)\nclass ReporterMiddlewareTest(BaseTest):\n    default_settings = copy.deepcopy(rollbar.SETTINGS)\n\n    def setUp(self):\n        importlib.reload(rollbar)\n        rollbar.SETTINGS = copy.deepcopy(self.default_settings)\n        rollbar.SETTINGS['handler'] = 'async'\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_catch_and_report_errors(self, mock_report):\n        from starlette.applications import Starlette\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/')\n        async def root(request):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        mock_report.assert_called_once()\n\n        args, kwargs = mock_report.call_args\n        self.assertEqual(kwargs, {})\n\n        exc_type, exc_value, exc_tb = args[0]\n\n        self.assertEqual(exc_type, ZeroDivisionError)\n        self.assertIsInstance(exc_value, ZeroDivisionError)\n\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_report_with_request_data(self, mock_report):\n        from starlette.applications import Starlette\n        from starlette.requests import Request\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/')\n        async def root(request):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        mock_report.assert_called_once()\n        request = mock_report.call_args[0][1]\n\n        self.assertIsInstance(request, Request)\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar._serialize_frame_data')\n    @mock.patch('rollbar.send_payload')\n    def test_should_send_payload_with_request_data(self, mock_send_payload, *mocks):\n        from starlette.applications import Starlette\n        from starlette.requests import Request\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/{path}')\n        async def root(request):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/test?param1=value1&param2=value2')\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n        payload_request = payload['data']['request']\n\n        self.assertEqual(payload_request['method'], 'GET')\n        self.assertEqual(payload_request['user_ip'], 'testclient')\n        self.assertEqual(\n            payload_request['url'],\n            'http://testserver/test?param1=value1&param2=value2',\n        )\n        self.assertDictEqual(payload_request['params'], {'path': 'test'})\n        self.assertDictEqual(\n            payload_request['GET'], {'param1': 'value1', 'param2': 'value2'}\n        )\n        self.assertDictEqual(\n            payload_request['headers'],\n            {\n                'accept': '*/*',\n                'accept-encoding': 'gzip, deflate',\n                'connection': 'keep-alive',\n                'host': 'testserver',\n                'user-agent': 'testclient',\n            },\n        )\n\n    @mock.patch('rollbar._check_config', return_value=True)\n    @mock.patch('rollbar.send_payload')\n    def test_should_add_framework_version_to_payload(self, mock_send_payload, *mocks):\n        import starlette\n        from starlette.applications import Starlette\n        import rollbar\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        self.assertIsNone(rollbar.BASE_DATA_HOOK)\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n        app.build_middleware_stack()\n\n        rollbar.report_exc_info()\n\n        mock_send_payload.assert_called_once()\n        payload = mock_send_payload.call_args[0][0]\n\n        self.assertIn('starlette', payload['data']['framework'])\n        self.assertIn(starlette.__version__, payload['data']['framework'])\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_default_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        from starlette.applications import Starlette\n        from starlette.testclient import TestClient\n        import rollbar\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        rollbar.SETTINGS['handler'] = 'default'\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/')\n        async def root(request):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        async_report_exc_info.assert_called_once()\n        sync_report_exc_info.assert_not_called()\n\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_async_report_exc_info_if_any_async_handler(\n        self, sync_report_exc_info, async_report_exc_info\n    ):\n        from starlette.applications import Starlette\n        from starlette.testclient import TestClient\n        import rollbar\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        rollbar.SETTINGS['handler'] = 'httpx'\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/')\n        async def root(request):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        async_report_exc_info.assert_called_once()\n        sync_report_exc_info.assert_not_called()\n\n    @mock.patch('logging.Logger.warning')\n    @mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)\n    @mock.patch('rollbar.report_exc_info')\n    def test_should_use_sync_report_exc_info_if_non_async_handlers(\n        self, sync_report_exc_info, async_report_exc_info, mock_log\n    ):\n        from starlette.applications import Starlette\n        from starlette.testclient import TestClient\n        import rollbar\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        rollbar.SETTINGS['handler'] = 'threading'\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/')\n        async def root(request):\n            1 / 0\n\n        client = TestClient(app)\n        with self.assertRaises(ZeroDivisionError):\n            client.get('/')\n\n        sync_report_exc_info.assert_called_once()\n        async_report_exc_info.assert_not_called()\n        mock_log.assert_called_once_with(\n            'Failed to report asynchronously. Trying to report synchronously.'\n        )\n\n    @unittest.skipUnless(\n        sys.version_info >= (3, 6), 'Global request access requires Python 3.6+'\n    )\n    @mock.patch('rollbar.contrib.starlette.middleware.store_current_request')\n    def test_should_store_current_request(self, store_current_request):\n        from starlette.applications import Starlette\n        from starlette.responses import PlainTextResponse\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n\n        expected_scope = {\n            'client': ['testclient', 50000],\n            'headers': [\n                (b'host', b'testserver'),\n                (b'accept', b'*/*'),\n                (b'accept-encoding', b'gzip, deflate'),\n                (b'connection', b'keep-alive'),\n                (b'user-agent', b'testclient'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/',\n            'query_string': b'',\n            'root_path': '',\n            'scheme': 'http',\n            'server': ['testserver', 80],\n            'type': 'http',\n        }\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/{param}')\n        async def root(request):\n            return PlainTextResponse('OK')\n\n        client = TestClient(app)\n        client.get('/')\n\n        store_current_request.assert_called_once()\n\n        scope = store_current_request.call_args[0][0]\n        self.assertEqual(scope, {**expected_scope, **scope})\n\n    @unittest.skipUnless(\n        sys.version_info >= (3, 6), 'Global request access is supported in Python 3.6+'\n    )\n    def test_should_return_current_request(self):\n        from starlette.applications import Starlette\n        from starlette.responses import PlainTextResponse\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n        from rollbar.contrib.starlette import get_current_request\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/')\n        async def root(original_request):\n            request = get_current_request()\n\n            self.assertEqual(get_public_attrs(request), get_public_attrs(original_request))\n\n            return PlainTextResponse('OK')\n\n        client = TestClient(app)\n        client.get('/')\n\n    @mock.patch('rollbar.contrib.starlette.requests.ContextVar', None)\n    @mock.patch('logging.Logger.error')\n    def test_should_not_return_current_request_for_older_python(self, mock_log):\n        from starlette.applications import Starlette\n        from starlette.responses import PlainTextResponse\n        from starlette.testclient import TestClient\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n        from rollbar.contrib.starlette import get_current_request\n\n        app = Starlette()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.route('/')\n        async def root(original_request):\n            request = get_current_request()\n\n            self.assertIsNone(request)\n            self.assertNotEqual(request, original_request)\n            mock_log.assert_called_once_with(\n                'Python 3.7+ (or aiocontextvars package)'\n                ' is required to receive current request.'\n            )\n\n            return PlainTextResponse('OK')\n\n        client = TestClient(app)\n        client.get('/')\n\n    def test_should_support_http_only(self):\n        from rollbar.contrib.starlette.middleware import ReporterMiddleware\n        from rollbar.lib._async import FailingTestASGIApp, run\n\n        testapp = ReporterMiddleware(FailingTestASGIApp())\n\n        with mock.patch('rollbar.report_exc_info') as mock_report:\n            with self.assertRaises(RuntimeError):\n                run(testapp({'type': 'http', 'headers': []}, None, None))\n\n            mock_report.assert_called_once()\n\n        with mock.patch('rollbar.report_exc_info') as mock_report:\n            with self.assertRaises(RuntimeError):\n                run(testapp({'type': 'websocket'}, None, None))\n\n            mock_report.assert_not_called()\n\n    def test_should_support_type_hints(self):\n        from starlette.types import Receive, Scope, Send\n\n        self.assertDictEqual(\n            rollbar.contrib.starlette.ReporterMiddleware.__call__.__annotations__,\n            {'scope': Scope, 'receive': Receive, 'send': Send, 'return': None},\n        )\n"
  },
  {
    "path": "rollbar/test/starlette_tests/test_requests.py",
    "content": "import sys\n\ntry:\n    import starlette\n\n    STARLETTE_INSTALLED = True\nexcept ImportError:\n    STARLETTE_INSTALLED = False\n\nimport unittest\n\nfrom rollbar.test import BaseTest\nfrom rollbar.test.utils import get_public_attrs\n\nALLOWED_PYTHON_VERSION = sys.version_info >= (3, 6)\n\n\n@unittest.skipUnless(\n    STARLETTE_INSTALLED and ALLOWED_PYTHON_VERSION,\n    'Global request access requires Python3.6+',\n)\nclass RequestTest(BaseTest):\n    def test_should_accept_request_param(self):\n        from starlette.requests import Request\n        from rollbar.contrib.starlette.requests import store_current_request\n        from rollbar.lib._async import async_receive\n\n        scope = {\n            'client': ['testclient', 50000],\n            'headers': [\n                (b'host', b'testserver'),\n                (b'user-agent', b'testclient'),\n                (b'accept-encoding', b'gzip, deflate'),\n                (b'accept', b'*/*'),\n                (b'connection', b'keep-alive'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/',\n            'query_string': b'',\n            'root_path': '',\n            'scheme': 'http',\n            'server': ['testserver', 80],\n            'type': 'http',\n        }\n        receive = async_receive(\n            {'type': 'http.request', 'body': b'body body', 'mode_body': False}\n        )\n        request = Request(scope, receive)\n\n        stored_request = store_current_request(request)\n\n        self.assertEqual(get_public_attrs(request), get_public_attrs(stored_request))\n\n    def test_should_accept_scope_param_if_http_type(self):\n        from starlette.requests import Request\n        from rollbar.contrib.starlette.requests import store_current_request\n        from rollbar.lib._async import async_receive\n\n        scope = {\n            'client': ['testclient', 50000],\n            'headers': [\n                (b'host', b'testserver'),\n                (b'user-agent', b'testclient'),\n                (b'accept-encoding', b'gzip, deflate'),\n                (b'accept', b'*/*'),\n                (b'connection', b'keep-alive'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/',\n            'query_string': b'',\n            'root_path': '',\n            'scheme': 'http',\n            'server': ['testserver', 80],\n            'type': 'http',\n        }\n        receive = async_receive(\n            {'type': 'http.request', 'body': b'body body', 'mode_body': False}\n        )\n        expected_request = Request(scope, receive)\n\n        request = store_current_request(scope, receive)\n\n        self.assertEqual(get_public_attrs(request), get_public_attrs(expected_request))\n\n    def test_should_not_accept_scope_param_if_not_http_type(self):\n        from rollbar.contrib.starlette.requests import store_current_request\n\n        scope = {'asgi': {'spec_version': '2.0', 'version': '3.0'}, 'type': 'lifespan'}\n        receive = {}\n\n        request = store_current_request(scope, receive)\n\n        self.assertIsNone(request)\n\n    def test_hasuser(self):\n        from starlette.requests import Request\n        from rollbar.contrib.starlette.requests import hasuser\n\n        request = Request({'type': 'http'}, {})\n        self.assertFalse(hasuser(request))\n\n        request = Request({'type': 'http', 'user': 'testuser'}, {})\n        self.assertTrue(hasuser(request))\n        self.assertEqual(request.user, 'testuser')\n"
  },
  {
    "path": "rollbar/test/test_basic_filters.py",
    "content": "from rollbar.lib import events, filters\n\nfrom rollbar.test import BaseTest\n\n\nclass BasicFiltersTest(BaseTest):\n    def setUp(self):\n        events.reset()\n        filters.add_builtin_filters({})\n\n    def test_rollbar_ignored_exception(self):\n        class IgnoredException(Exception):\n            _rollbar_ignore = True\n\n        class NotIgnoredException(Exception):\n            _rollbar_ignore = False\n\n        self.assertFalse(events.on_exception_info((None, IgnoredException(), None)))\n        self.assertIsNot(events.on_exception_info((None, NotIgnoredException(), None)), False)\n\n    def test_filter_by_level(self):\n        self.assertFalse(events.on_exception_info((None, 123, None), level='ignored'))\n        self.assertIsNot(events.on_exception_info((None, 123, None), level='error'), False)\n\n        self.assertFalse(events.on_message('hello world', level='ignored'))\n        self.assertIsNot(events.on_message('hello world', level='error'), False)\n\n        self.assertFalse(events.on_payload({}, level='ignored'))\n        self.assertIsNot(events.on_message({}, level='error'), False)\n"
  },
  {
    "path": "rollbar/test/test_batched_transform.py",
    "content": "from rollbar.lib.transforms import transform\nfrom rollbar.lib.transform import Transform\nfrom rollbar.lib.traverse import traverse\n\nfrom rollbar.test import BaseTest\n\n\nclass TrackingTransformer(Transform):\n    def __init__(self):\n        self.got = []\n\n    def default(self, o, key=None):\n        self.got.append((o, key))\n        return o\n\n\nclass BatchedTransformTest(BaseTest):\n    def assertTrackingTransform(self, input):\n        tracking_transformer = TrackingTransformer()\n\n        transforms = [\n            tracking_transformer,\n            tracking_transformer,\n        ]\n\n        transform(input, transforms, batch_transforms=True)\n\n        want = []\n\n        def dup_watch_handler(o, key=None):\n            want.append((o, key))\n            want.append((o, key))\n            return o\n\n        traverse(\n            input,\n            string_handler=dup_watch_handler,\n            tuple_handler=dup_watch_handler,\n            namedtuple_handler=dup_watch_handler,\n            list_handler=dup_watch_handler,\n            set_handler=dup_watch_handler,\n            mapping_handler=dup_watch_handler,\n            default_handler=dup_watch_handler,\n            circular_reference_handler=dup_watch_handler,\n        )\n\n        self.assertEqual(want, tracking_transformer.got)\n\n    def test_number(self):\n        self.assertTrackingTransform(1)\n\n    def test_flat_list(self):\n        self.assertTrackingTransform([0, 1, 2, 3])\n\n    def test_flat_tuple(self):\n        self.assertTrackingTransform((0, 1, 2, 3))\n\n    def test_nested_object(self):\n        self.assertTrackingTransform((0, [1, 2], {\"a\": 3, \"b\": (4, 5)}))\n"
  },
  {
    "path": "rollbar/test/test_custom_filters.py",
    "content": "import re\n\nfrom rollbar.lib import events, filters\n\nfrom rollbar.test import BaseTest\n\n\nclass CustomFiltersTest(BaseTest):\n    def setUp(self):\n        events.reset()\n        filters.add_builtin_filters({})\n\n    def test_ignore_by_setting_rollbar_ignore(self):\n        class NotIgnoredByDefault(Exception):\n            pass\n\n        def _ignore_if_cruel_world_filter(exc_info, **kw):\n            cls, exc, trace = exc_info\n            if 'cruel world' in str(exc):\n                exc._rollbar_ignore = True\n\n            return exc_info\n\n        events.add_exception_info_handler(_ignore_if_cruel_world_filter, pos=0)\n\n        self.assertIsNot(events.on_exception_info((None, NotIgnoredByDefault('hello world'), None)), False)\n        self.assertFalse(events.on_exception_info((None, NotIgnoredByDefault('hello cruel world'), None)))\n\n    def test_ignore_messages_by_regex(self):\n        regex = re.compile(r'cruel')\n\n        def _ignore_cruel_world_substring(message, **kw):\n            if regex.search(message):\n                return False\n\n            return message\n\n        events.add_message_handler(_ignore_cruel_world_substring)\n\n        self.assertFalse(events.on_message('hello cruel world'))\n        self.assertIsNot(events.on_message('hello world'), False)\n\n    def test_modify_payload(self):\n        def _add_test_key(payload, **kw):\n            payload['test'] = 333\n            return payload\n\n        events.add_payload_handler(_add_test_key)\n\n        self.assertEqual(events.on_payload({'hello': 'world'}), {'hello': 'world', 'test': 333})\n"
  },
  {
    "path": "rollbar/test/test_lib.py",
    "content": "from rollbar.lib import dict_merge, prefix_match, key_match, key_depth\nfrom rollbar.lib.transport import _get_proxy_cfg\n\nfrom rollbar.test import BaseTest\n\n\nclass RollbarLibTest(BaseTest):\n    def test_prefix_match(self):\n        key = ['password', 'argspec', '0']\n        self.assertTrue(prefix_match(key, [['password']]))\n\n    def test_prefix_match_dont_match(self):\n        key = ['environ', 'argspec', '0']\n        self.assertFalse(prefix_match(key, [['password']]))\n\n    def test_key_match(self):\n        canonical = ['body', 'trace', 'frames', '*', 'locals', '*']\n        key = ['body', 'trace', 'frames', 5, 'locals', 'foo']\n\n        self.assertTrue(key_match(key, canonical))\n\n    def test_key_match_dont_match(self):\n        canonical = ['body', 'trace', 'frames', '*', 'locals', '*']\n        key = ['body', 'trace', 'frames', 5, 'bar', 'foo']\n\n        self.assertFalse(key_match(key, canonical))\n\n    def test_key_match_wildcard_end(self):\n        canonical = ['body', 'trace', 'frames', '*', 'locals', '*']\n        key = ['body', 'trace', 'frames', 5, 'locals', 'foo', 'bar']\n\n        self.assertTrue(key_match(key, canonical))\n\n    def test_key_match_too_short(self):\n        canonical = ['body', 'trace', 'frames', '*', 'locals', '*']\n        key = ['body', 'trace', 'frames', 5, 'locals']\n\n        self.assertFalse(key_match(key, canonical))\n\n    def test_key_depth(self):\n        canonicals = [['body', 'trace', 'frames', '*', 'locals', '*']]\n        key = ['body', 'trace', 'frames', 5, 'locals', 'foo']\n\n        self.assertEqual(6, key_depth(key, canonicals))\n\n    def test_key_depth_dont_match(self):\n        canonicals = [['body', 'trace', 'frames', '*', 'locals', '*']]\n        key = ['body', 'trace', 'frames', 5, 'bar', 'foo']\n\n        self.assertEqual(0, key_depth(key, canonicals))\n\n    def test_key_depth_wildcard_end(self):\n        canonicals = [['body', 'trace', 'frames', '*']]\n        key = ['body', 'trace', 'frames', 5, 'locals', 'foo', 'bar']\n\n        self.assertEqual(4, key_depth(key, canonicals))\n\n    def test_dict_merge_not_dict(self):\n        a = {'a': {'b': 42}}\n        b = 99\n        result = dict_merge(a, b)\n\n        self.assertEqual(99, result)\n\n    def test_dict_merge_dicts_independent(self):\n        a = {'a': {'b': 42}}\n        b = {'x': {'y': 99}}\n        result = dict_merge(a, b)\n\n        self.assertIn('a', result)\n        self.assertIn('b', result['a'])\n        self.assertEqual(42, result['a']['b'])\n        self.assertIn('x', result)\n        self.assertIn('y', result['x'])\n        self.assertEqual(99, result['x']['y'])\n\n    def test_dict_merge_dicts(self):\n        a = {'a': {'b': 42}}\n        b = {'a': {'c': 99}}\n        result = dict_merge(a, b)\n\n        self.assertIn('a', result)\n        self.assertIn('b', result['a'])\n        self.assertIn('c', result['a'])\n        self.assertEqual(42, result['a']['b'])\n        self.assertEqual(99, result['a']['c'])\n\n    def test_dict_merge_dicts_second_wins(self):\n        a = {'a': {'b': 42}}\n        b = {'a': {'b': 99}}\n        result = dict_merge(a, b)\n\n        self.assertIn('a', result)\n        self.assertIn('b', result['a'])\n        self.assertEqual(99, result['a']['b'])\n\n    def test_dict_merge_dicts_select_poll(self):\n        import select\n        poll = getattr(select, 'poll', None)\n        if poll is None:\n            return\n        p = poll()\n        a = {'a': {'b': 42}}\n        b = {'a': {'y': p}}\n        result = dict_merge(a, b, silence_errors=True)\n\n        self.assertIn('a', result)\n        self.assertIn('b', result['a'])\n        self.assertEqual(42, result['a']['b'])\n        self.assertIn('y', result['a'])\n        self.assertRegex(result['a']['y'], r'Uncopyable obj')\n\n    def test_transport_get_proxy_cfg(self):\n        result = _get_proxy_cfg({})\n        self.assertEqual(None, result)\n\n        result = _get_proxy_cfg({'proxy': 'localhost'})\n        self.assertEqual({'http': 'http://localhost', 'https': 'http://localhost'}, result)\n\n        result = _get_proxy_cfg({'proxy': 'localhost:8080'})\n        self.assertEqual({'http': 'http://localhost:8080', 'https': 'http://localhost:8080'}, result)\n\n        result = _get_proxy_cfg({'proxy': 'localhost', 'proxy_user': 'username', 'proxy_password': 'password'})\n        self.assertEqual({\n            'http': 'http://username:password@localhost',\n            'https': 'http://username:password@localhost',\n        }, result)\n"
  },
  {
    "path": "rollbar/test/test_loghandler.py",
    "content": "\"\"\"\nTests for the RollbarHandler logging handler\n\"\"\"\nimport copy\nimport json\nimport logging\nimport sys\n\nfrom unittest import mock\n\nimport rollbar\nfrom rollbar.logger import RollbarHandler\n\nfrom rollbar.test import BaseTest\n\n\n_test_access_token = 'aaaabbbbccccddddeeeeffff00001111'\n_test_environment = 'test'\n_default_settings = copy.deepcopy(rollbar.SETTINGS)\n\nclass CauseException(Exception):\n    pass\n\n\nclass LogHandlerTest(BaseTest):\n    def setUp(self):\n        rollbar._initialized = False\n        rollbar.SETTINGS = copy.deepcopy(_default_settings)\n        self.logger = logging.getLogger(__name__)\n        self.logger.setLevel(logging.DEBUG)\n\n        self.rollbar_handler = RollbarHandler(_test_access_token, _test_environment)\n        self.rollbar_handler.setLevel(logging.WARNING)\n\n        self.logger.addHandler(self.rollbar_handler)\n\n    def tearDown(self):\n        self.logger.removeHandler(self.rollbar_handler)\n\n    @mock.patch('rollbar.send_payload')\n    def test_message_gets_formatted(self, send_payload):\n        self.logger.warning(\"Hello %d %s\", 1, 'world')\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['data']['body']['message']['body'], \"Hello 1 world\")\n        self.assertEqual(payload['data']['body']['message']['args'], (1, 'world'))\n        self.assertEqual(payload['data']['body']['message']['record']['name'], __name__)\n        self.assertEqual(payload['data']['custom']['args'], (1, 'world'))\n        self.assertEqual(payload['data']['custom']['record']['name'], __name__)\n\n    @mock.patch('rollbar.send_payload')\n    def test_string_or_int_level(self, send_payload):\n        self.logger.setLevel(logging.ERROR)\n        self.rollbar_handler.setLevel('WARNING')\n        self.logger.error(\"I am an error\")\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['data']['level'], 'error')\n\n        self.rollbar_handler.setLevel(logging.WARNING)\n        self.logger.error(\"I am an error\")\n\n        self.assertEqual(payload['data']['level'], 'error')\n\n    def test_request_is_get_from_log_record_if_present(self):\n        # Request objects vary depending on python frameworks or packages.\n        # Using a dictionary for this test is enough.\n        request = {\"fake\": \"request\", \"for\":  \"testing purporse\"}\n\n        # No need to test request parsing and payload sent,\n        # just need to be sure that proper rollbar function is called\n        # with passed request as argument.\n        with mock.patch(\"rollbar.report_message\") as report_message_mock:\n            self.logger.warning(\"Warning message\", extra={\"request\": request})\n            self.assertEqual(report_message_mock.call_args[1][\"request\"], request)\n\n        # if you call logger.exception outside of an exception\n        # handler, it shouldn't try to report exc_info, since it\n        # won't have any\n        with mock.patch(\"rollbar.report_exc_info\") as report_exc_info:\n            with mock.patch(\"rollbar.report_message\") as report_message_mock:\n                self.logger.exception(\"Exception message\", extra={\"request\": request})\n                report_exc_info.assert_not_called()\n                self.assertEqual(report_message_mock.call_args[1][\"request\"], request)\n\n        with mock.patch(\"rollbar.report_exc_info\") as report_exc_info:\n            with mock.patch(\"rollbar.report_message\") as report_message_mock:\n                try:\n                    raise Exception()\n                except:\n                    self.logger.exception(\"Exception message\", extra={\"request\": request})\n                    self.assertEqual(report_exc_info.call_args[1][\"request\"], request)\n                    report_message_mock.assert_not_called()\n\n    @mock.patch('rollbar.send_payload')\n    def test_nested_exception_trace_chain(self, send_payload):\n        def _raise_context():\n            bar_local = 'bar'\n            raise CauseException('bar')\n\n        def _raise_ex():\n            try:\n                _raise_context()\n            except CauseException as context:\n                # python2 won't automatically assign this traceback...\n                exc_info = sys.exc_info()\n                setattr(context, '__traceback__', exc_info[2])\n                try:\n                    foo_local = 'foo'\n                    # in python3 __context__ is automatically set when an exception is raised in an except block\n                    e = Exception('foo')\n                    setattr(e, '__context__', context)  # PEP-3134\n                    raise e\n                except:\n                    self.logger.exception(\"Bad time\")\n\n        _raise_ex()\n\n        self.assertEqual(send_payload.called, True)\n        payload = send_payload.call_args[0][0]\n        body = payload['data']['body']\n        trace = body['trace'] if 'trace' in body else None\n        trace_chain = body['trace_chain'] if 'trace_chain' in body else None\n        has_only_trace_chain = trace is None and trace_chain is not None\n        has_only_trace = trace is not None and trace_chain is None\n        self.assertTrue(has_only_trace or has_only_trace_chain)\n        if trace_chain is not None:\n            self.assertEqual('Bad time', payload['data']['custom']['exception']['description'])\n        if trace is not None:\n            self.assertEqual('Bad time', trace['exception']['description'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_not_nested_exception_trace_chain(self, send_payload):\n        def _raise_context():\n            bar_local = 'bar'\n            raise CauseException('bar')\n\n        def _raise_ex():\n            try:\n                _raise_context()\n            except:\n                self.logger.exception(\"Bad time\")\n\n        _raise_ex()\n\n        self.assertEqual(send_payload.called, True)\n        payload = send_payload.call_args[0][0]\n        body = payload['data']['body']\n        trace = body['trace'] if 'trace' in body else None\n        trace_chain = body['trace_chain'] if 'trace_chain' in body else None\n        has_only_trace_chain = trace is None and trace_chain is not None\n        has_only_trace = trace is not None and trace_chain is None\n        self.assertTrue(has_only_trace or has_only_trace_chain)\n        if trace_chain is not None:\n            self.assertEqual('Bad time', payload['data']['custom']['exception']['description'])\n        if trace is not None:\n            self.assertEqual('Bad time', trace['exception']['description'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_logging_extra(self, send_payload):\n        self.logger.error(\"Test error\", extra=dict(test_attribute=1, test_other='test'))\n\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['body']['message'].get('test_attribute'), 1)\n        self.assertEqual(payload['data']['body']['message'].get('test_other'), 'test')\n        self.assertEqual(payload['data']['custom'].get('test_attribute'), 1)\n        self.assertEqual(payload['data']['custom'].get('test_other'), 'test')\n\n    @mock.patch('rollbar.send_payload')\n    def test_logging_extra_data(self, send_payload):\n        self.logger.error(\n            \"Test error\",\n            extra=dict(extra_data=dict(test_attribute=1, test_other='test')))\n\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['body']['message'].get('test_attribute'), 1)\n        self.assertEqual(payload['data']['body']['message'].get('test_other'), 'test')\n        self.assertEqual(payload['data']['custom'].get('test_attribute'), 1)\n        self.assertEqual(payload['data']['custom'].get('test_other'), 'test')\n\n    @mock.patch('rollbar.send_payload')\n    def test_log_formatting(self, send_payload):\n        self.rollbar_handler.formatter = logging.Formatter(\n            '%(test_other)s[%(test_attribute)s]: %(message)s'\n        )\n        self.logger.error(\"Test error\", extra=dict(test_attribute=1, test_other='test'))\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['body']['message']['body'], 'test[1]: Test error')\n"
  },
  {
    "path": "rollbar/test/test_pyramid.py",
    "content": "from unittest import mock\n\nfrom rollbar.test import BaseTest\n\ntry:\n    from pyramid.request import Request\n\n    PYRAMID_INSTALLED = True\nexcept ImportError:\n    PYRAMID_INSTALLED = False\n\n\nif PYRAMID_INSTALLED:\n\n    class PyramidMiddlewareTest(BaseTest):\n        def test_catch_exception_in_the_wsgi_app(self):\n            from rollbar.contrib.pyramid import RollbarMiddleware\n\n            def wsgi_app(environ, start_resp):\n                raise RuntimeError(\"oops\")\n\n            middleware = RollbarMiddleware({}, wsgi_app)\n\n            with mock.patch(\"rollbar.report_exc_info\") as mock_report:\n                with self.assertRaises(RuntimeError):\n                    middleware(environ={}, start_resp=lambda: None)\n\n            self.assertEqual(mock_report.call_count, 1)\n\n            args, kwargs = mock_report.call_args\n            self.assertEqual(kwargs, {})\n\n            exc_info, request = args\n\n            exc_type, exc_value, exc_tb = exc_info\n            self.assertEqual(exc_type, RuntimeError)\n            self.assertIsInstance(exc_value, RuntimeError)\n\n            self.assertIsInstance(request, Request)\n"
  },
  {
    "path": "rollbar/test/test_rollbar.py",
    "content": "import base64\nimport copy\nimport json\nimport socket\nimport threading\nimport uuid\n\nimport sys\nfrom collections import namedtuple\n\nfrom rollbar.lib.session import reset_current_session\n\ntry:\n    from StringIO import StringIO\nexcept ImportError:\n    from io import StringIO\n\nfrom pathlib import Path\n\nfrom unittest import mock\n\nimport unittest\n\nimport rollbar\nfrom rollbar.lib import string_types\n\nfrom rollbar.test import BaseTest\nfrom rollbar.test.utils import get_public_attrs\n\ntry:\n    eval(\"\"\"\n        def _anonymous_tuple_func(x, (a, b), y):\n            ret = x + a + b + y\n            breakme()\n            return ret\n    \"\"\")\nexcept SyntaxError:\n    _anonymous_tuple_func = None\n\n\n_test_access_token = 'aaaabbbbccccddddeeeeffff00001111'\n_default_settings = copy.deepcopy(rollbar.SETTINGS)\n\n\nclass RollbarTest(BaseTest):\n    def setUp(self):\n        rollbar._initialized = False\n        rollbar.SETTINGS = copy.deepcopy(_default_settings)\n        rollbar.init(_test_access_token, locals={'enabled': True}, dummy_key='asdf', handler='blocking', timeout=12345)\n        reset_current_session()\n\n    def test_merged_settings(self):\n        expected = {'enabled': True, 'sizes': rollbar.DEFAULT_LOCALS_SIZES, 'safe_repr': True, 'scrub_varargs': True, 'safelisted_types': [], 'whitelisted_types': []}\n        self.assertDictEqual(rollbar.SETTINGS['locals'], expected)\n        self.assertEqual(rollbar.SETTINGS['timeout'], 12345)\n        self.assertEqual(rollbar.SETTINGS['dummy_key'], 'asdf')\n\n    def test_default_configuration(self):\n        self.assertEqual(rollbar.SETTINGS['access_token'], _test_access_token)\n        self.assertEqual(rollbar.SETTINGS['environment'], 'production')\n\n    @mock.patch('rollbar.send_payload')\n    def test_disabled(self, send_payload):\n        rollbar.SETTINGS['enabled'] = False\n\n        rollbar.report_message('foo')\n        try:\n            raise Exception('foo')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, False)\n\n    def test_server_data(self):\n        server_data = rollbar._build_server_data()\n\n        self.assertIn('host', server_data)\n        self.assertIn('argv', server_data)\n        self.assertNotIn('branch', server_data)\n        self.assertNotIn('root', server_data)\n        self.assertGreater(len(server_data['host']), 2)\n\n        rollbar.SETTINGS['host'] = 'test-host'\n        rollbar.SETTINGS['branch'] = 'master'\n        rollbar.SETTINGS['root'] = '/home/test/'\n\n        server_data = rollbar._build_server_data()\n\n        self.assertIn('argv', server_data)\n        self.assertEqual(server_data['host'], 'test-host')\n        self.assertEqual(server_data['branch'], 'master')\n        self.assertEqual(server_data['root'], '/home/test/')\n\n    def test_wsgi_request_data(self):\n        rollbar.SETTINGS['include_request_body'] = True\n        request = {\n            'CONTENT_LENGTH': str(len('body body body')),\n            'CONTENT_TYPE': '',\n            'DOCUMENT_URI': '/api/test',\n            'GATEWAY_INTERFACE': 'CGI/1.1',\n            'HTTP_CONNECTION': 'close',\n            'HTTP_HOST': 'example.com',\n            'HTTP_USER_AGENT': 'Agent',\n            'PATH_INFO': '/api/test',\n            'QUERY_STRING': 'format=json&param1=value1&param2=value2',\n            'REMOTE_ADDR': '127.0.0.1',\n            'REQUEST_METHOD': 'GET',\n            'SCRIPT_NAME': '',\n            'SERVER_ADDR': '127.0.0.1',\n            'SERVER_NAME': 'example.com',\n            'SERVER_PORT': '80',\n            'SERVER_PROTOCOL': 'HTTP/1.1',\n            'wsgi.input': StringIO('body body body'),\n            'wsgi.multiprocess': True,\n            'wsgi.multithread': False,\n            'wsgi.run_once': False,\n            'wsgi.url_scheme': 'http',\n            'wsgi.version': (1, 0)\n        }\n        data = rollbar._build_wsgi_request_data(request)\n        self.assertEqual(data['url'], 'http://example.com/api/test?format=json&param1=value1&param2=value2')\n        self.assertEqual(data['user_ip'], '127.0.0.1')\n        self.assertEqual(data['method'], 'GET')\n        self.assertEqual(data['body'], 'body body body')\n        self.assertDictEqual(data['GET'], {'format': 'json', 'param1': 'value1', 'param2': 'value2'})\n        self.assertDictEqual(data['headers'], {'Connection': 'close', 'Host': 'example.com', 'User-Agent': 'Agent'})\n\n    def test_wsgi_request_data_no_body(self):\n        rollbar.SETTINGS['include_request_body'] = False\n        request = {\n            'CONTENT_LENGTH': str(len('body body body')),\n            'REMOTE_ADDR': '127.0.0.1',\n            'SERVER_NAME': 'example.com',\n            'SERVER_PORT': '80',\n            'wsgi.input': StringIO('body body body'),\n            'wsgi.url_scheme': 'http',\n        }\n        data = rollbar._build_wsgi_request_data(request)\n        self.assertNotIn('body', data)\n        rollbar.SETTINGS['include_request_body'] = True\n\n    def test_starlette_request_data(self):\n        try:\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette to be installed')\n\n        scope = {\n            'type': 'http',\n            'client': ('127.0.0.1', 1453),\n            'headers': [\n                (b'accept', b'*/*'),\n                (b'content-type', b'application/x-www-form-urlencoded'),\n                (b'host', b'example.com'),\n                (b'user-agent', b'Agent'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/api/test',\n            'path_params': {'param': 'test'},\n            'query_params': {\n                'format': 'json',\n                'param1': 'value1',\n                'param2': 'value2',\n            },\n            'query_string': b'format=json&param1=value1&param2=value2',\n            'scheme': 'http',\n            'server': ('example.com', 80),\n            'url': {'path': 'example.com'},\n        }\n        request = Request(scope)\n        data = rollbar._build_starlette_request_data(request)\n\n        self.assertEqual(data['url'], 'http://example.com/api/test?format=json&param1=value1&param2=value2')\n        self.assertEqual(data['user_ip'], '127.0.0.1')\n        self.assertEqual(data['method'], 'GET')\n        self.assertDictEqual(data['params'], {'param': 'test'})\n        self.assertDictEqual(data['GET'], {'format': 'json', 'param1': 'value1', 'param2': 'value2'})\n        self.assertDictEqual(\n            data['headers'],\n            {\n                'accept': '*/*',\n                'content-type': 'application/x-www-form-urlencoded',\n                'host': 'example.com',\n                'user-agent': 'Agent',\n            },\n        )\n\n    def test_starlette_request_baggage_headers(self):\n        try:\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette to be installed')\n\n        scope = {\n            'type': 'http',\n            'client': ('127.0.0.1', 1453),\n            'headers': [\n                (b'accept', b'*/*'),\n                (b'content-type', b'application/x-www-form-urlencoded'),\n                (b'host', b'example.com'),\n                (b'user-agent', b'Agent'),\n                (b'baggage', b'rollbar.session.id=abcde, rollbar.execution.scope.id = fghij'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/api/test',\n            'path_params': {'param': 'test'},\n            'query_params': {\n                'format': 'json',\n                'param1': 'value1',\n                'param2': 'value2',\n            },\n            'query_string': b'format=json&param1=value1&param2=value2',\n            'scheme': 'http',\n            'server': ('example.com', 80),\n            'url': {'path': 'example.com'},\n        }\n        data = {}\n        request = Request(scope)\n        rollbar._add_request_data(data, request)\n        rollbar._add_session_data(data)\n\n        self.assertEqual(data['attributes'], [\n            {'key': 'session_id', 'value': 'abcde'},\n            {'key': 'execution_scope_id', 'value': 'fghij'},\n        ])\n\n    def test_starlette_request_data_with_consumed_body(self):\n        try:\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette to be installed')\n        from rollbar.lib._async import async_receive, run\n\n        rollbar.SETTINGS['include_request_body'] = True\n        body = b'body body body'\n        scope = {\n            'type': 'http',\n            'client': ('127.0.0.1', 1453),\n            'headers': [\n                (b'content-type', b'text/html'),\n                (b'content-length', str(len(body)).encode('latin-1')),\n            ],\n            'method': 'GET',\n            'path': '/api/test',\n            'query_string': b'',\n        }\n        receive = async_receive(\n            {'type': 'http.request', 'body': body, 'mode_body': False}\n        )\n        request = Request(scope, receive)\n\n        # Consuming body in Starlette middleware is currently disabled\n        run(request.body()) # await request.body()\n\n        data = rollbar._build_starlette_request_data(request)\n\n        self.assertEqual(data['body'], body.decode('latin-1'))\n\n    def test_starlette_request_data_empty_values(self):\n        try:\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette to be installed')\n\n        scope = {\n            'type': 'http',\n            'client': ('127.0.0.1', 1453),\n            'headers': [\n                (b'content-type', b'text/html'),\n            ],\n            'method': 'GET',\n            'query_string': b'',\n            'path': '',\n        }\n        request = Request(scope)\n\n        data = rollbar._build_starlette_request_data(request)\n\n        self.assertFalse('GET' in data)\n        self.assertFalse('url' in data)\n        self.assertFalse('params' in data)\n        self.assertTrue('headers' in data)\n        self.assertEqual(data['user_ip'], scope['client'][0])\n        self.assertEqual(data['method'], scope['method'])\n\n    def test_fastapi_request_data(self):\n        try:\n            from fastapi.requests import Request\n        except ImportError:\n            self.skipTest('Requires FastAPI to be installed')\n\n        scope = {\n            'type': 'http',\n            'client': ('127.0.0.1', 1453),\n            'headers': [\n                (b'accept', b'*/*'),\n                (b'content-type', b'application/x-www-form-urlencoded'),\n                (b'host', b'example.com'),\n                (b'user-agent', b'Agent'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/api/test',\n            'path_params': {'param': 'test'},\n            'query_params': {\n                'format': 'json',\n                'param1': 'value1',\n                'param2': 'value2',\n            },\n            'query_string': b'format=json&param1=value1&param2=value2',\n            'scheme': 'http',\n            'server': ('example.com', 80),\n            'url': {'path': 'example.com'},\n        }\n        request = Request(scope)\n        data = rollbar._build_fastapi_request_data(request)\n\n        self.assertEqual(data['url'], 'http://example.com/api/test?format=json&param1=value1&param2=value2')\n        self.assertEqual(data['user_ip'], '127.0.0.1')\n        self.assertEqual(data['method'], 'GET')\n        self.assertDictEqual(data['params'], {'param': 'test'})\n        self.assertDictEqual(data['GET'], {'format': 'json', 'param1': 'value1', 'param2': 'value2'})\n        self.assertDictEqual(\n            data['headers'],\n            {\n                'accept': '*/*',\n                'content-type': 'application/x-www-form-urlencoded',\n                'host': 'example.com',\n                'user-agent': 'Agent',\n            },\n        )\n\n    def test_fastapi_request_baggage_headers(self):\n        try:\n            from fastapi.requests import Request\n        except ImportError:\n            self.skipTest('Requires FastAPI to be installed')\n\n        scope = {\n            'type': 'http',\n            'client': ('127.0.0.1', 1453),\n            'headers': [\n                (b'accept', b'*/*'),\n                (b'content-type', b'application/x-www-form-urlencoded'),\n                (b'host', b'example.com'),\n                (b'user-agent', b'Agent'),\n                (b'baggage', b'rollbar.session.id=abcde, rollbar.execution.scope.id = fghij'),\n            ],\n            'http_version': '1.1',\n            'method': 'GET',\n            'path': '/api/test',\n            'path_params': {'param': 'test'},\n            'query_params': {\n                'format': 'json',\n                'param1': 'value1',\n                'param2': 'value2',\n            },\n            'query_string': b'format=json&param1=value1&param2=value2',\n            'scheme': 'http',\n            'server': ('example.com', 80),\n            'url': {'path': 'example.com'},\n        }\n        data = {}\n        request = Request(scope)\n        rollbar._add_request_data(data, request)\n        rollbar._add_session_data(data)\n\n        self.assertEqual(data['attributes'], [\n            {'key': 'session_id', 'value': 'abcde'},\n            {'key': 'execution_scope_id', 'value': 'fghij'},\n        ])\n\n    def test_fastapi_request_data_with_consumed_body(self):\n        try:\n            from fastapi import Request\n        except ImportError:\n            self.skipTest('Requires FastAPI to be installed')\n        from rollbar.lib._async import async_receive, run\n\n        rollbar.SETTINGS['include_request_body'] = True\n        body = b'body body body'\n        scope = {\n            'type': 'http',\n            'headers': [\n                (b'content-type', b'text/html'),\n                (b'content-length', str(len(body)).encode('latin-1')),\n            ],\n            'method': 'GET',\n            'path': '/api/test',\n            'query_string': b'',\n        }\n        receive = async_receive(\n            {'type': 'http.request', 'body': body, 'mode_body': False}\n        )\n        request = Request(scope, receive)\n\n        # Consuming body in FastAPI middlewares is currently disabled\n        run(request.body()) # await request.body()\n\n        data = rollbar._build_fastapi_request_data(request)\n\n        self.assertEqual(data['body'], body.decode('latin-1'))\n\n    def test_fastapi_request_data_empty_values(self):\n        try:\n            from fastapi import Request\n        except ImportError:\n            self.skipTest('Requires FastAPI to be installed')\n\n        scope = {\n            'type': 'http',\n            'client': ('127.0.0.1', 1453),\n            'headers': [\n                (b'content-type', b'text/html'),\n            ],\n            'method': 'GET',\n            'query_string': b'',\n            'path': '',\n        }\n        request = Request(scope)\n\n        data = rollbar._build_fastapi_request_data(request)\n\n        self.assertFalse('GET' in data)\n        self.assertFalse('url' in data)\n        self.assertFalse('params' in data)\n        self.assertTrue('headers' in data)\n        self.assertEqual(data['user_ip'], scope['client'][0])\n        self.assertEqual(data['method'], scope['method'])\n\n    def test_django_build_person_data(self):\n        try:\n            import django\n            from django.conf import settings\n        except ImportError:\n            self.skipTest('Requires Django to be installed')\n\n        if not settings.configured:\n            settings.configure(\n                INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes'],\n                SERVER_NAME = 'example.com',\n            )\n            if django.VERSION >= (1, 7):\n                django.setup()\n\n        from django.contrib.auth.models import User\n        from django.http.request import HttpRequest\n\n        request = HttpRequest()\n        request.user = User()\n        request.user.id = 123\n        request.user.username = 'admin'\n        request.user.email = 'admin@example.org'\n\n        data = rollbar._build_person_data(request)\n\n        self.assertDictEqual(\n            data, {'id': '123', 'username': 'admin', 'email': 'admin@example.org'}\n        )\n\n    def test_django_baggage_headers(self):\n        try:\n            import django\n            from django.conf import settings\n        except ImportError:\n            self.skipTest('Requires Django to be installed')\n\n        if not settings.configured:\n            settings.configure(\n                INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes'],\n                DEFAULT_CHARSET='utf-8',\n                ALLOWED_HOSTS = ['example.com'],\n            )\n            django.setup()\n\n        from django.http.request import HttpRequest\n        request = HttpRequest()\n        request.META['HTTP_BAGGAGE'] = 'rollbar.session.id=abcde, rollbar.execution.scope.id = fghij'\n        request.META['SERVER_NAME'] = 'example.com'\n        request.META['SERVER_PORT'] = 80\n        request.META['REMOTE_ADDR'] = '0.0.0.0'\n\n        data = dict()\n        rollbar._add_request_data(data, request)\n        rollbar._add_session_data(data)\n\n        self.assertEqual(data['attributes'], [\n            {'key': 'session_id', 'value': 'abcde'},\n            {'key': 'execution_scope_id', 'value': 'fghij'},\n        ])\n\n    def test_starlette_build_person_data_if_user_authenticated(self):\n        try:\n            from starlette.authentication import SimpleUser\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette to be installed')\n\n        # Implement interface with the id attribute\n        class User(SimpleUser):\n            counter = 0\n\n            def __init__(self, username, email):\n                super().__init__(username)\n                self.email = email\n\n                User.counter += 1\n                self.id = User.counter\n\n        scope = {'type': 'http'}\n        request = Request(scope)\n        # Make the user authenticated\n        request.scope['user'] = User('admin', 'admin@example.org')\n\n        data = rollbar._build_person_data(request)\n\n        self.assertDictEqual(\n            data, {'id': '1', 'username': 'admin', 'email': 'admin@example.org'}\n        )\n\n    def test_starlette_failsafe_build_person_data_if_user_not_authenticated(self):\n        try:\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette to be installed')\n\n        scope = {'type': 'http'}\n        request = Request(scope)\n\n        data = rollbar._build_person_data(request)\n\n        self.assertIsNone(data)\n\n    @unittest.skipUnless(sys.version_info >= (3, 6), 'Python3.6+ required')\n    def test_get_request_starlette_middleware(self):\n        try:\n            from starlette.applications import Starlette\n            from starlette.middleware import Middleware\n            from starlette.responses import PlainTextResponse\n            from starlette.routing import Route\n            from starlette.testclient import TestClient\n        except ImportError:\n            self.skipTest('Requires Starlette package')\n        from rollbar.contrib.starlette import ReporterMiddleware\n\n        def root(starlette_request):\n            current_request = rollbar.get_request()\n\n            self.assertEqual(get_public_attrs(current_request), get_public_attrs(starlette_request))\n\n            return PlainTextResponse(\"bye bye\")\n\n        routes = [Route('/{param}', root)]\n        middleware = [Middleware(ReporterMiddleware)]\n        app = Starlette(routes=routes, middleware=middleware)\n        client = TestClient(app)\n        response = client.get('/test?param1=value1&param2=value2')\n\n        self.assertEqual(response.status_code, 200)\n\n    @unittest.skipUnless(sys.version_info >= (3, 6), 'Python3.6+ required')\n    def test_get_request_starlette_logger(self):\n        try:\n            from starlette.applications import Starlette\n            from starlette.middleware import Middleware\n            from starlette.responses import PlainTextResponse\n            from starlette.routing import Route\n            from starlette.testclient import TestClient\n        except ImportError:\n            self.skipTest('Requires Starlette package')\n        from rollbar.contrib.starlette import ReporterMiddleware\n\n        def root(starlette_request):\n            current_request = rollbar.get_request()\n\n            self.assertEqual(get_public_attrs(current_request), get_public_attrs(starlette_request))\n\n            return PlainTextResponse(\"bye bye\")\n\n        routes = [Route('/{param}', root)]\n        middleware = [Middleware(ReporterMiddleware)]\n        app = Starlette(routes=routes, middleware=middleware)\n        client = TestClient(app)\n        response = client.get('/test?param1=value1&param2=value2')\n\n        self.assertEqual(response.status_code, 200)\n\n    @unittest.skipUnless(sys.version_info >= (3, 6), 'Python3.6+ required')\n    def test_get_request_fastapi_middleware(self):\n        try:\n            from fastapi import FastAPI, Request\n            from fastapi.testclient import TestClient\n        except ImportError:\n            self.skipTest('Requires FastaAPI package')\n        from rollbar.contrib.fastapi import ReporterMiddleware\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/{param}')\n        def root(param, fastapi_request: Request):\n            current_request = rollbar.get_request()\n\n            self.assertEqual(get_public_attrs(current_request), get_public_attrs(fastapi_request))\n\n        root = fastapi_add_route_with_request_param(\n            app, root, '/{param}', 'fastapi_request'\n        )\n\n        client = TestClient(app)\n        response = client.get('/test?param1=value1&param2=value2')\n\n        self.assertEqual(response.status_code, 200)\n\n    @unittest.skipUnless(sys.version_info >= (3, 6), 'Python3.6+ required')\n    def test_get_request_fastapi_logger(self):\n        try:\n            from fastapi import FastAPI, Request\n            from fastapi.testclient import TestClient\n        except ImportError:\n            self.skipTest('Requires FastaAPI package')\n        from rollbar.contrib.fastapi import ReporterMiddleware\n\n        app = FastAPI()\n        app.add_middleware(ReporterMiddleware)\n\n        @app.get('/{param}')\n        def root(fastapi_request: Request):\n            current_request = rollbar.get_request()\n\n            self.assertEqual(get_public_attrs(current_request), get_public_attrs(fastapi_request))\n\n        root = fastapi_add_route_with_request_param(\n            app, root, '/{param}', 'fastapi_request'\n        )\n\n        client = TestClient(app)\n        response = client.get('/test?param1=value1&param2=value2')\n\n        self.assertEqual(response.status_code, 200)\n\n    @unittest.skipUnless(sys.version_info >= (3, 6), 'Python3.6+ required')\n    def test_get_request_fastapi_router(self):\n        try:\n            import fastapi\n            from fastapi import FastAPI, Request\n            from fastapi.testclient import TestClient\n        except ImportError:\n            self.skipTest('Requires FastAPI package')\n        from rollbar.contrib.fastapi import add_to as rollbar_add_to\n\n        if fastapi.__version__ < '0.41.0':\n            self.skipTest('Requires FastAPI 0.41.0+')\n\n        app = FastAPI()\n        rollbar_add_to(app)\n\n        @app.get('/{param}')\n        def root(fastapi_request: Request):\n            current_request = rollbar.get_request()\n\n            self.assertEqual(get_public_attrs(current_request), get_public_attrs(fastapi_request))\n\n        root = fastapi_add_route_with_request_param(\n            app, root, '/{param}', 'fastapi_request'\n        )\n\n        client = TestClient(app)\n        response = client.get('/test?param1=value1&param2=value2')\n\n        self.assertEqual(response.status_code, 200)\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_exception(self, send_payload):\n\n        def _raise():\n            try:\n                raise Exception('foo')\n            except:\n                rollbar.report_exc_info()\n\n        _raise()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['access_token'], _test_access_token)\n        self.assertIn('body', payload['data'])\n        self.assertIn('trace', payload['data']['body'])\n        self.assertNotIn('trace_chain', payload['data']['body'])\n        self.assertIn('exception', payload['data']['body']['trace'])\n        self.assertEqual(payload['data']['body']['trace']['exception']['message'], 'foo')\n        self.assertEqual(payload['data']['body']['trace']['exception']['class'], 'Exception')\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('locals', payload['data']['body']['trace']['frames'][-1])\n\n    @mock.patch('rollbar._post_api')\n    def test_lambda_function_good(self, _post_api):\n        rollbar.SETTINGS['handler'] = 'thread'\n        fake_event = {'a': 42}\n        fake_context = MockLambdaContext(99)\n        @rollbar.lambda_function\n        def my_lambda_func(event, context):\n            return [event['a'], context.x]\n\n        result = my_lambda_func(fake_event, fake_context)\n\n        self.assertEqual(len(result), 2)\n        self.assertEqual(result[0], 42)\n        self.assertEqual(result[1], 99)\n        self.assertEqual(_post_api.called, False)\n\n        rollbar._CURRENT_LAMBDA_CONTEXT = None\n        rollbar.SETTINGS['handler'] = 'blocking'\n\n    @mock.patch('rollbar._post_api')\n    def test_lambda_function_bad(self, _post_api):\n        rollbar.SETTINGS['handler'] = 'thread'\n        fake_event = {'a': 42}\n        fake_context = MockLambdaContext(99)\n        @rollbar.lambda_function\n        def my_lambda_func(event, context):\n            raise event['a']\n\n        result = None\n        try:\n            result = my_lambda_func(fake_event, fake_context)\n        except:\n            pass\n\n        self.assertEqual(result, None)\n        self.assertEqual(_post_api.called, True)\n\n        rollbar._CURRENT_LAMBDA_CONTEXT = None\n        rollbar.SETTINGS['handler'] = 'blocking'\n\n    @mock.patch('rollbar._post_api')\n    def test_lambda_function_method_good(self, _post_api):\n        rollbar.SETTINGS['handler'] = 'thread'\n        fake_event = {'a': 42}\n        fake_context = MockLambdaContext(99)\n\n        class LambdaClass(object):\n            def __init__(self):\n                self.a = 13\n\n            def my_lambda_func(self, event, context):\n                return [event['a'], context.x, self.a]\n\n        app = LambdaClass()\n        app.my_lambda_func = rollbar.lambda_function(app.my_lambda_func)\n        result = app.my_lambda_func(fake_event, fake_context)\n\n        self.assertEqual(len(result), 3)\n        self.assertEqual(result[0], 42)\n        self.assertEqual(result[1], 99)\n        self.assertEqual(result[2], 13)\n        self.assertEqual(_post_api.called, False)\n\n        rollbar._CURRENT_LAMBDA_CONTEXT = None\n        rollbar.SETTINGS['handler'] = 'blocking'\n\n    @mock.patch('rollbar._post_api')\n    def test_lambda_function_method_bad(self, _post_api):\n        rollbar.SETTINGS['handler'] = 'thread'\n        fake_event = {'a': 42}\n        fake_context = MockLambdaContext(99)\n\n        class LambdaClass(object):\n            def __init__(self):\n                self.a = 13\n\n            def my_lambda_func(self, event, context):\n                raise self.a\n\n        app = LambdaClass()\n        app.my_lambda_func = rollbar.lambda_function(app.my_lambda_func)\n\n        result = None\n        try:\n            result = app.my_lambda_func(fake_event, fake_context)\n        except:\n            pass\n\n        self.assertEqual(result, None)\n        self.assertEqual(_post_api.called, True)\n\n        rollbar._CURRENT_LAMBDA_CONTEXT = None\n        rollbar.SETTINGS['handler'] = 'blocking'\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_exception_with_cause(self, send_payload):\n        def _raise_cause():\n            bar_local = 'bar'\n            raise CauseException('bar')\n\n        def _raise_ex():\n            try:\n                _raise_cause()\n            except CauseException as cause:\n                # python2 won't automatically assign this traceback...\n                exc_info = sys.exc_info()\n                setattr(cause, '__traceback__', exc_info[2])\n\n                try:\n                    foo_local = 'foo'\n                    # in python3 this would normally be expressed as\n                    # raise Exception('foo') from cause\n                    e = Exception('foo')\n                    setattr(e, '__cause__', cause)  # PEP-3134\n                    raise e\n                except:\n                    rollbar.report_exc_info()\n\n        _raise_ex()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['access_token'], _test_access_token)\n        self.assertIn('body', payload['data'])\n        self.assertNotIn('trace', payload['data']['body'])\n        self.assertIn('trace_chain', payload['data']['body'])\n        self.assertEqual(2, len(payload['data']['body']['trace_chain']))\n\n        self.assertIn('exception', payload['data']['body']['trace_chain'][0])\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['exception']['message'], 'foo')\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['exception']['class'], 'Exception')\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['frames'][-1]['locals']['foo_local'], 'foo')\n\n        self.assertIn('exception', payload['data']['body']['trace_chain'][1])\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['exception']['message'], 'bar')\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['exception']['class'], 'CauseException')\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['frames'][-1]['locals']['bar_local'], 'bar')\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_exception_with_same_exception_as_cause(self, send_payload):\n        cause_exc = CauseException('bar')\n\n        def _raise_cause():\n            bar_local = 'bar'\n            raise cause_exc\n\n        def _raise_ex():\n            try:\n                _raise_cause()\n            except CauseException as cause:\n                # python2 won't automatically assign this traceback...\n                exc_info = sys.exc_info()\n                setattr(cause, '__traceback__', exc_info[2])\n\n                try:\n                    foo_local = 'foo'\n                    # in python3 this would normally be expressed as\n                    # raise cause from cause\n                    setattr(cause, '__cause__', cause)  # PEP-3134\n                    raise cause\n                except:\n                    rollbar.report_exc_info()\n\n        ex_raiser = threading.Thread(target=_raise_ex)\n        ex_raiser.daemon = True\n        ex_raiser.start()\n        # 0.5 seconds ought be enough for any modern computer to get into the\n        # cyclical parts of the code, but not so long as to collect a lot of\n        # objects in memory\n        ex_raiser.join(timeout=0.5)\n\n        if ex_raiser.is_alive():\n            # This breaks the circular reference, allowing thread to exit and\n            # to be joined\n            cause_exc.__cause__ = None\n            ex_raiser.join()\n            self.fail('Cyclic reference in rollbar._walk_trace_chain()')\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['access_token'], _test_access_token)\n        self.assertIn('body', payload['data'])\n        self.assertNotIn('trace', payload['data']['body'])\n        self.assertIn('trace_chain', payload['data']['body'])\n        self.assertEqual(2, len(payload['data']['body']['trace_chain']))\n\n        self.assertIn('exception', payload['data']['body']['trace_chain'][0])\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['exception']['message'], 'bar')\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['exception']['class'], 'CauseException')\n        frames = payload['data']['body']['trace_chain'][0]['frames']\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['frames'][0]['locals']['foo_local'], 'foo')\n\n        self.assertIn('exception', payload['data']['body']['trace_chain'][1])\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['exception']['message'], 'bar')\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['exception']['class'], 'CauseException')\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['frames'][-1]['locals']['bar_local'], 'bar')\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_exception_with_context(self, send_payload):\n        def _raise_context():\n            bar_local = 'bar'\n            raise CauseException('bar')\n\n        def _raise_ex():\n            try:\n                _raise_context()\n            except CauseException as context:\n                # python2 won't automatically assign this traceback...\n                exc_info = sys.exc_info()\n                setattr(context, '__traceback__', exc_info[2])\n\n                try:\n                    foo_local = 'foo'\n                    # in python3 __context__ is automatically set when an exception is raised in an except block\n                    e = Exception('foo')\n                    setattr(e, '__context__', context)  # PEP-3134\n                    raise e\n                except:\n                    rollbar.report_exc_info()\n\n        _raise_ex()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['access_token'], _test_access_token)\n        self.assertIn('body', payload['data'])\n        self.assertNotIn('trace', payload['data']['body'])\n        self.assertIn('trace_chain', payload['data']['body'])\n        self.assertEqual(2, len(payload['data']['body']['trace_chain']))\n\n        self.assertIn('exception', payload['data']['body']['trace_chain'][0])\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['exception']['message'], 'foo')\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['exception']['class'], 'Exception')\n        self.assertEqual(payload['data']['body']['trace_chain'][0]['frames'][-1]['locals']['foo_local'], 'foo')\n\n        self.assertIn('exception', payload['data']['body']['trace_chain'][1])\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['exception']['message'], 'bar')\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['exception']['class'], 'CauseException')\n        self.assertEqual(payload['data']['body']['trace_chain'][1]['frames'][-1]['locals']['bar_local'], 'bar')\n\n    @mock.patch('rollbar.send_payload')\n    def test_exception_filters(self, send_payload):\n\n        rollbar.SETTINGS['exception_level_filters'] = [\n            (OSError, 'ignored'),\n            ('rollbar.ApiException', 'ignored'),\n            ('bogus.DoesntExist', 'ignored'),\n        ]\n\n        def _raise_exception():\n            try:\n                raise Exception('foo')\n            except:\n                rollbar.report_exc_info()\n\n        def _raise_os_error():\n            try:\n                raise OSError('bar')\n            except:\n                rollbar.report_exc_info()\n\n        def _raise_api_exception():\n            try:\n                raise rollbar.ApiException('bar')\n            except:\n                rollbar.report_exc_info()\n\n        _raise_exception()\n        self.assertTrue(send_payload.called)\n\n        _raise_os_error()\n        self.assertEqual(1, send_payload.call_count)\n\n        _raise_api_exception()\n        self.assertEqual(1, send_payload.call_count)\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_messsage(self, send_payload):\n        rollbar.report_message('foo')\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['access_token'], _test_access_token)\n        self.assertIn('body', payload['data'])\n        self.assertIn('message', payload['data']['body'])\n        self.assertIn('body', payload['data']['body']['message'])\n        self.assertEqual(payload['data']['body']['message']['body'], 'foo')\n\n    @mock.patch('rollbar.send_payload')\n    def test_uuid(self, send_payload):\n        uuid = rollbar.report_message('foo')\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual(payload['data']['uuid'], uuid)\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_exc_info_level(self, send_payload):\n\n        try:\n            raise Exception('level_error')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['level'], 'error')\n\n        try:\n            raise Exception('level_info')\n        except:\n            rollbar.report_exc_info(level='info')\n\n        self.assertEqual(send_payload.called, True)\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['level'], 'info')\n\n        # payload takes precendence over 'level'\n        try:\n            raise Exception('payload_warn')\n        except:\n            rollbar.report_exc_info(level='info', payload_data={'level': 'warn'})\n\n        self.assertEqual(send_payload.called, True)\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['level'], 'warn')\n\n    @mock.patch('rollbar.send_payload')\n    def test_report_exc_info_nones(self, send_payload):\n\n        rollbar.report_exc_info(exc_info=(None, None, None))\n\n        self.assertEqual(send_payload.called, True)\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['level'], 'error')\n\n    @mock.patch('rollbar._send_failsafe')\n    @mock.patch('rollbar.lib.transport.post',\n                side_effect=lambda *args, **kw: MockResponse({'status': 'Payload Too Large'}, 413))\n    def test_trigger_failsafe(self, post, _send_failsafe):\n        rollbar.report_message('derp')\n        self.assertEqual(_send_failsafe.call_count, 1)\n\n        try:\n            raise Exception('trigger_failsafe')\n        except:\n            rollbar.report_exc_info()\n            self.assertEqual(_send_failsafe.call_count, 2)\n\n    @mock.patch('rollbar._send_failsafe')\n    @mock.patch('rollbar.lib.transport.post',\n                side_effect=lambda *args, **kw: MockRawResponse('<html>\\r\\n' \\\n                                                             '<head><title>502 Bad Gateway</title></head>\\r\\n' \\\n                                                             '<body bgcolor=\"white\">\\r\\n' \\\n                                                             '<center><h1>502 Bad Gateway</h1></center>\\r\\n' \\\n                                                             '<hr><center>nginx</center>\\r\\n' \\\n                                                             '</body>\\r\\n' \\\n                                                             '</html>\\r\\n', 502))\n    def test_502_failsafe(self, post, _send_failsafe):\n        rollbar.report_message('derp')\n        # self.assertEqual(_send_failsafe.call_count, 1)\n\n        try:\n            raise Exception('trigger_failsafe')\n        except:\n            rollbar._post_api('/api/1/item', {'derp'})\n\n    @mock.patch('rollbar.send_payload')\n    def test_send_failsafe(self, send_payload):\n        test_uuid = str(uuid.uuid4())\n        test_host = socket.gethostname()\n        test_data = {\n            'access_token': _test_access_token,\n            'data': {\n                'body': {\n                    'message': {\n                        'body': 'Failsafe from pyrollbar: test message. '\n                                'Original payload may be found in your server '\n                                'logs by searching for the UUID.'\n                    }\n                },\n                'failsafe': True,\n                'level': 'error',\n                'custom': {\n                    'orig_host': test_host,\n                    'orig_uuid': test_uuid\n                },\n                'environment': rollbar.SETTINGS['environment'],\n                'internal': True,\n                'notifier': rollbar.SETTINGS['notifier']\n            }\n        }\n\n        rollbar._send_failsafe('test message', test_uuid, test_host)\n        self.assertEqual(send_payload.call_count, 1)\n        self.assertEqual(send_payload.call_args[0][0], test_data)\n\n    @mock.patch('rollbar.log.exception')\n    @mock.patch('rollbar.send_payload', side_effect=Exception('Monkey Business!'))\n    def test_fail_to_send_failsafe(self, send_payload, mock_log):\n        test_uuid = str(uuid.uuid4())\n        test_host = socket.gethostname()\n        rollbar._send_failsafe('test message', test_uuid, test_host)\n        self.assertEqual(mock_log.call_count, 1)\n\n    @unittest.skipUnless(rollbar.AsyncHTTPClient, 'Requires async handler to be installed')\n    @mock.patch('rollbar._send_payload_async')\n    def test_async_handler(self, send_payload_async):\n        def _raise():\n            try:\n                raise Exception('foo')\n            except:\n                rollbar.report_exc_info()\n\n        rollbar.SETTINGS['handler'] = 'async'\n        _raise()\n\n        send_payload_async.assert_called_once()\n\n    @unittest.skipUnless(rollbar.httpx, 'Requires HTTPX to be installed')\n    @mock.patch('rollbar._send_payload_httpx')\n    def test_httpx_handler(self, send_payload_httpx):\n        def _raise():\n            try:\n                raise Exception('foo')\n            except:\n                rollbar.report_exc_info()\n\n        rollbar.SETTINGS['handler'] = 'async'\n        _raise()\n\n        send_payload_httpx.assert_called_once()\n\n    @unittest.skipUnless(sys.version_info >= (3, 6), 'assert_called_once support requires Python3.6+')\n    @mock.patch('rollbar._send_payload_thread_pool')\n    def test_thread_pool_handler(self, send_payload_thread_pool):\n        def _raise():\n            try:\n                raise Exception('foo')\n            except:\n                rollbar.report_exc_info()\n        rollbar.SETTINGS['handler'] = 'thread_pool'\n        _raise()\n\n        send_payload_thread_pool.assert_called_once()\n\n    @unittest.skipUnless(sys.version_info >= (3, 2), 'concurrent.futures support requires Python3.2+')\n    def test_thread_pool_submit(self):\n        from rollbar.lib.thread_pool import init_pool, submit\n        init_pool(1)\n        ran = {'nope': True}  # dict used so it is not shadowed in run\n\n        def run(payload_str, access_token):\n            ran['nope'] = False\n\n        submit(run, 'foo', 'bar')\n        self.assertFalse(ran['nope'])\n\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_constructor(self, send_payload):\n\n        class tmp(object):\n            def __init__(self, arg1):\n                self.arg1 = arg1\n                foo()\n\n        try:\n            t = tmp(33)\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        self.assertEqual('arg1', payload['data']['body']['trace']['frames'][-1]['argspec'][1])\n        self.assertEqual(33, payload['data']['body']['trace']['frames'][-1]['locals']['arg1'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_failed_locals_serialization(self, send_payload):\n\n        class tmp(object):\n            @property\n            def __class__(self):\n                foo()\n\n        try:\n            t = tmp()\n            raise Exception('trigger_serialize')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_no_args(self, send_payload):\n\n        _raise = lambda: foo()\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('locals', payload['data']['body']['trace']['frames'][-1])\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_with_args(self, send_payload):\n\n        _raise = lambda arg1, arg2: foo(arg1, arg2)\n\n        try:\n            _raise('arg1-value', 'arg2-value')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('arg1', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual('arg1-value', payload['data']['body']['trace']['frames'][-1]['locals']['arg1'])\n        self.assertEqual('arg2', payload['data']['body']['trace']['frames'][-1]['argspec'][1])\n        self.assertEqual('arg2-value', payload['data']['body']['trace']['frames'][-1]['locals']['arg2'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_with_defaults(self, send_payload):\n\n        _raise = lambda arg1='default': foo(arg1)\n\n        try:\n            _raise(arg1='arg1-value')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        # NOTE(cory): Lambdas are a bit strange. We treat default values for lambda args\n        #             as positional.\n        self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('arg1', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual('arg1-value', payload['data']['body']['trace']['frames'][-1]['locals']['arg1'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_with_star_args(self, send_payload):\n\n        _raise = lambda *args: foo(arg1)\n\n        try:\n            _raise('arg1-value')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        varargs = payload['data']['body']['trace']['frames'][-1]['varargspec']\n\n        self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['locals'][varargs]))\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][varargs][0], r'\\*+')\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_with_star_args_and_args(self, send_payload):\n\n        _raise = lambda arg1, *args: foo(arg1)\n\n        try:\n            _raise('arg1-value', 1, 2)\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        varargs = payload['data']['body']['trace']['frames'][-1]['varargspec']\n\n        self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('arg1', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual('arg1-value', payload['data']['body']['trace']['frames'][-1]['locals']['arg1'])\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['locals'][varargs]))\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][varargs][0], r'\\*+')\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][varargs][1], r'\\*+')\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_with_kwargs(self, send_payload):\n\n        _raise = lambda **kwargs: foo(arg1)\n\n        try:\n            _raise(arg1='arg1-value', arg2=2)\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        keywords = payload['data']['body']['trace']['frames'][-1]['keywordspec']\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['locals'][keywords]))\n        self.assertEqual('arg1-value', payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['arg1'])\n        self.assertEqual(2, payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['arg2'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_with_kwargs_and_args(self, send_payload):\n\n        _raise = lambda arg1, arg2, **kwargs: foo(arg1)\n\n        try:\n            _raise('a1', 'a2', arg3='arg3-value', arg4=2)\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        keywords = payload['data']['body']['trace']['frames'][-1]['keywordspec']\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('arg1', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual('arg2', payload['data']['body']['trace']['frames'][-1]['argspec'][1])\n        self.assertEqual('a1', payload['data']['body']['trace']['frames'][-1]['locals']['arg1'])\n        self.assertEqual('a2', payload['data']['body']['trace']['frames'][-1]['locals']['arg2'])\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['locals'][keywords]))\n        self.assertEqual('arg3-value', payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['arg3'])\n        self.assertEqual(2, payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['arg4'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_lambda_with_kwargs_and_args_and_defaults(self, send_payload):\n\n        _raise = lambda arg1, arg2, arg3='default-value', **kwargs: foo(arg1)\n\n        try:\n            _raise('a1', 'a2', arg3='arg3-value', arg4=2)\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        keywords = payload['data']['body']['trace']['frames'][-1]['keywordspec']\n\n        # NOTE(cory): again, default values are strange for lambdas and we include them as\n        #             positional args.\n        self.assertEqual(3, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('arg1', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual('arg2', payload['data']['body']['trace']['frames'][-1]['argspec'][1])\n        self.assertEqual('arg3', payload['data']['body']['trace']['frames'][-1]['argspec'][2])\n        self.assertEqual('a1', payload['data']['body']['trace']['frames'][-1]['locals']['arg1'])\n        self.assertEqual('a2', payload['data']['body']['trace']['frames'][-1]['locals']['arg2'])\n        self.assertEqual('arg3-value', payload['data']['body']['trace']['frames'][-1]['locals']['arg3'])\n\n        self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['locals'][keywords]))\n        self.assertEqual(2, payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['arg4'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_args_generators(self, send_payload):\n\n        def _raise(arg1):\n            for i in range(2):\n                if i > 0:\n                    raise Exception()\n                else:\n                    yield i\n\n        try:\n            l = list(_raise('hello world'))\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('arg1', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual('hello world', payload['data']['body']['trace']['frames'][-1]['locals']['arg1'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_anonymous_tuple_args(self, send_payload):\n\n        # Only run this test on Python versions that support it\n        if not _anonymous_tuple_func:\n            return\n\n        try:\n            _anonymous_tuple_func((1, (2, 3), 4))\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        self.assertEqual(4, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual(1, payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual(2, payload['data']['body']['trace']['frames'][-1]['argspec'][1])\n        self.assertEqual(3, payload['data']['body']['trace']['frames'][-1]['argspec'][2])\n        self.assertEqual(4, payload['data']['body']['trace']['frames'][-1]['argspec'][3])\n        self.assertEqual(10, payload['data']['body']['trace']['frames'][-1]['locals']['ret'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_scrub_defaults(self, send_payload):\n\n        def _raise(password='sensitive', clear='text'):\n            headers = { 'Authorization': 'bearer 123' }\n\n            raise Exception()\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('kwargs', payload['data']['body']['trace']['frames'][-1]['locals'])\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('password', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['password'], r'\\*+')\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['headers']['Authorization'], r'\\*+')\n        self.assertEqual('clear', payload['data']['body']['trace']['frames'][-1]['argspec'][1])\n        self.assertEqual('text', payload['data']['body']['trace']['frames'][-1]['locals']['clear'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_dont_scrub_star_args(self, send_payload):\n        rollbar.SETTINGS['locals']['scrub_varargs'] = False\n\n        def _raise(*args):\n            raise Exception()\n\n        try:\n            _raise('sensitive', 'text')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('locals', payload['data']['body']['trace']['frames'][-1])\n\n        varargspec = payload['data']['body']['trace']['frames'][-1]['varargspec']\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['locals'][varargspec]))\n        self.assertEqual(payload['data']['body']['trace']['frames'][-1]['locals'][varargspec][0], 'sensitive')\n        self.assertEqual(payload['data']['body']['trace']['frames'][-1]['locals'][varargspec][1], 'text')\n\n    @mock.patch('rollbar.send_payload')\n    def test_scrub_kwargs(self, send_payload):\n\n        def _raise(**kwargs):\n            raise Exception()\n\n        try:\n            _raise(password='sensitive', clear='text')\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        keywords = payload['data']['body']['trace']['frames'][-1]['keywordspec']\n\n        self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['locals'][keywords]))\n        self.assertIn('password', payload['data']['body']['trace']['frames'][-1]['locals'][keywords])\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['password'], r'\\*+')\n        self.assertIn('clear', payload['data']['body']['trace']['frames'][-1]['locals'][keywords])\n        self.assertEqual('text', payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['clear'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_scrub_locals(self, send_payload):\n        invalid_b64 = b'CuX2JKuXuLVtJ6l1s7DeeQ=='\n        invalid = base64.b64decode(invalid_b64)\n\n        def _raise():\n            # Make sure that the _invalid local variable makes its\n            # way into the payload even if its value cannot be serialized\n            # properly.\n            _invalid = invalid\n\n            # Make sure the Password field gets scrubbed even though its\n            # original value could not be serialized properly.\n            Password = invalid\n\n            password = 'sensitive'\n            raise Exception((_invalid, Password, password))\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['password'], r'\\*+')\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['Password'], r'\\*+')\n        self.assertIn('_invalid', payload['data']['body']['trace']['frames'][-1]['locals'])\n\n        undecodable_message = '<Undecodable type:(%s) base64:(%s)>' % ('bytes', base64.b64encode(invalid).decode('ascii'))\n        self.assertEqual(undecodable_message, payload['data']['body']['trace']['frames'][-1]['locals']['_invalid'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_scrub_namedtuple(self, send_payload):\n\n        SomeTuple = namedtuple('SomeTuple', ['password', 'some_field'])\n\n        def _raise():\n            Data = SomeTuple(password='clear_text', some_field='some_field')\n\n            password = 'sensitive'\n            raise Exception((Data, password))\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['password'], r'\\*+')\n        self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['Data'], 'password=\\'\\*+\\'')\n\n    @mock.patch('rollbar.send_payload')\n    def test_scrub_nans(self, send_payload):\n        def _raise():\n            infinity = float('Inf')\n            negative_infinity = float('-Inf')\n            not_a_number = float('NaN')\n            raise Exception()\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual('<Infinity>', payload['data']['body']['trace']['frames'][-1]['locals']['infinity'])\n        self.assertEqual('<NegativeInfinity>', payload['data']['body']['trace']['frames'][-1]['locals']['negative_infinity'])\n        self.assertEqual('<NaN>', payload['data']['body']['trace']['frames'][-1]['locals']['not_a_number'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_scrub_self_referencing(self, send_payload):\n        def _raise(obj):\n            raise Exception()\n\n        try:\n            obj = {'x': 42.3}\n            obj['child'] = {\n                'parent': obj\n            }\n\n            # NOTE(cory): We copy the dict here so that we don't produce a circular reference\n            # from the _rase() args.\n            _raise(dict(obj))\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertTrue(\n            (isinstance(payload['data']['body']['trace']['frames'][-1]['locals']['obj'], dict) and\n             'child' in payload['data']['body']['trace']['frames'][-1]['locals']['obj'])\n\n             or\n\n            (isinstance(payload['data']['body']['trace']['frames'][-1]['locals']['obj'], string_types) and\n             payload['data']['body']['trace']['frames'][-1]['locals']['obj'].startswith('<CircularReference'))\n        )\n\n        self.assertTrue(\n            (isinstance(payload['data']['body']['trace']['frames'][-1]['locals']['obj'], dict) and\n             'x' in payload['data']['body']['trace']['frames'][-1]['locals']['obj'] and\n             payload['data']['body']['trace']['frames'][-1]['locals']['obj']['x'] == 42.3)\n\n             or\n\n            (isinstance(payload['data']['body']['trace']['frames'][-1]['locals']['obj'], string_types) and\n             payload['data']['body']['trace']['frames'][-1]['locals']['obj'].startswith('<CircularReference'))\n        )\n\n    @mock.patch('rollbar.send_payload')\n    def test_scrub_local_ref(self, send_payload):\n        \"\"\"\n        NOTE(cory): This test checks to make sure that we do not scrub a local variable that is a reference\n                    to a parameter that is scrubbed.\n                    Ideally we would be able to scrub 'copy' as well since we know that it has the same\n                    value as a field that was scrubbed.\n        \"\"\"\n        def _raise(password='sensitive'):\n            copy = password\n            raise Exception()\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertEqual('sensitive', payload['data']['body']['trace']['frames'][-1]['locals']['copy'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_large_arg_val(self, send_payload):\n\n        def _raise(large):\n            raise Exception()\n\n        try:\n            large = ''.join(['#'] * 200)\n            _raise(large)\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n        self.assertEqual('large', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertEqual(\"################################################...#################################################\",\n                         payload['data']['body']['trace']['frames'][-1]['locals']['large'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_long_list_arg_val(self, send_payload):\n\n        def _raise(large):\n            raise Exception()\n\n        try:\n            xlarge = ['hi' for _ in range(30)]\n            # NOTE(cory): We copy the list here so that the local variables from\n            # this frame are not referenced directly by the frame from _raise()\n            # call above. If we didn't copy this list, Rollbar would report a\n            # circular reference for the args on _raise().\n            _raise([str(x) for x in xlarge])\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['argspec']))\n\n        self.assertEqual('large', payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n        self.assertTrue(\n            (['hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', '...'] ==\n                payload['data']['body']['trace']['frames'][-1]['argspec'][0])\n\n            or\n\n            (['hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi', '...'] ==\n                    payload['data']['body']['trace']['frames'][0]['locals']['xlarge']))\n\n\n    @mock.patch('rollbar.send_payload')\n    def test_last_frame_has_locals(self, send_payload):\n\n        def _raise():\n            some_var = 'some value'\n            raise Exception()\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        self.assertNotIn('argspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('varargspec', payload['data']['body']['trace']['frames'][-1])\n        self.assertNotIn('keywordspec', payload['data']['body']['trace']['frames'][-1])\n\n        self.assertIn('locals', payload['data']['body']['trace']['frames'][-1])\n        self.assertIn('some_var', payload['data']['body']['trace']['frames'][-1]['locals'])\n        self.assertEqual(\"some value\",\n                         payload['data']['body']['trace']['frames'][-1]['locals']['some_var'])\n\n\n    @mock.patch('rollbar.send_payload')\n    def test_all_project_frames_have_locals(self, send_payload):\n\n        prev_root = rollbar.SETTINGS['root']\n        rollbar.SETTINGS['root'] = __file__.rstrip('pyc')\n        try:\n            step1()\n        except:\n            rollbar.report_exc_info()\n        finally:\n            rollbar.SETTINGS['root'] = prev_root\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n        for frame in payload['data']['body']['trace']['frames']:\n            self.assertIn('locals', frame)\n\n\n    @mock.patch('rollbar.send_payload')\n    def test_only_last_frame_has_locals(self, send_payload):\n\n        prev_root = rollbar.SETTINGS['root']\n        rollbar.SETTINGS['root'] = 'dummy'\n        try:\n            step1()\n        except:\n            rollbar.report_exc_info()\n        finally:\n            rollbar.SETTINGS['root'] = prev_root\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        num_frames = len(payload['data']['body']['trace']['frames'])\n        for i, frame in enumerate(payload['data']['body']['trace']['frames']):\n            if i < num_frames - 1:\n                self.assertNotIn('locals', frame)\n            else:\n                self.assertIn('locals', frame)\n\n\n    @mock.patch('rollbar.send_payload')\n    def test_modify_arg(self, send_payload):\n        # Record locals for all frames\n        prev_root = rollbar.SETTINGS['root']\n        rollbar.SETTINGS['root'] = __file__.rstrip('pyc')\n        try:\n            called_with('original value')\n        except:\n            rollbar.report_exc_info()\n        finally:\n            rollbar.SETTINGS['root'] = prev_root\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n\n        frames = payload['data']['body']['trace']['frames']\n        called_with_frame = frames[1]\n\n        self.assertEqual('arg1', called_with_frame['argspec'][0])\n        self.assertEqual('changed', called_with_frame['locals']['arg1'])\n\n    @mock.patch('rollbar.send_payload')\n    def test_unicode_exc_info(self, send_payload):\n        message = '\\u221a'\n\n        try:\n            raise Exception(message)\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(send_payload.called, True)\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['body']['trace']['exception']['message'], message)\n\n    @mock.patch('rollbar.lib.transport.post', side_effect=lambda *args, **kw: MockResponse({'status': 'OK'}, 200))\n    def test_serialize_and_send_payload(self, post=None):\n        invalid_b64 = b'CuX2JKuXuLVtJ6l1s7DeeQ=='\n        invalid = base64.b64decode(invalid_b64)\n\n        def _raise():\n            # Make sure that the _invalid local variable makes its\n            # way into the payload even if its value cannot be serialized\n            # properly.\n            _invalid = invalid\n\n            # Make sure the Password field gets scrubbed even though its\n            # original value could not be serialized properly.\n            Password = invalid\n\n            password = 'sensitive'\n            raise Exception('bug bug')\n\n        try:\n            _raise()\n        except:\n            rollbar.report_exc_info()\n\n        self.assertEqual(post.called, True)\n        payload_data = post.call_args[1]['data']\n        self.assertIsInstance(payload_data, str)\n        self.assertIn('bug bug', payload_data)\n\n        try:\n            post.call_args[1]['data']\n        except:\n            self.assertTrue(False)\n\n    def test_scrub_webob_request_data(self):\n        rollbar._initialized = False\n        rollbar.init(_test_access_token, locals={'enabled': True}, dummy_key='asdf', handler='blocking', timeout=12345,\n            scrub_fields=rollbar.SETTINGS['scrub_fields'] + ['token', 'secret', 'cookies', 'authorization'])\n\n        import webob\n        request = webob.Request.blank('/the/path?q=hello&password=hunter2',\n                                      base_url='http://example.com',\n                                      headers={\n                                          'X-Real-Ip': '5.6.7.8',\n                                          'Cookies': 'name=value; password=hash;',\n                                          'Authorization': 'I am from NSA'\n                                      },\n                                      POST='foo=bar&confirm_password=hunter3&token=secret')\n\n        unscrubbed = rollbar._build_webob_request_data(request)\n        self.assertEqual(unscrubbed['url'], 'http://example.com/the/path?q=hello&password=hunter2')\n        self.assertEqual(unscrubbed['user_ip'], '5.6.7.8')\n        self.assertDictEqual(unscrubbed['GET'], {'q': 'hello', 'password': 'hunter2'})\n        self.assertDictEqual(unscrubbed['POST'], {'foo': 'bar', 'confirm_password': 'hunter3', 'token': 'secret'})\n        self.assertEqual('5.6.7.8', unscrubbed['headers']['X-Real-Ip'])\n        self.assertEqual('name=value; password=hash;', unscrubbed['headers']['Cookies'])\n        self.assertEqual('I am from NSA', unscrubbed['headers']['Authorization'])\n\n        scrubbed = rollbar._transform(unscrubbed)\n        self.assertRegex(scrubbed['url'], r'http://example.com/the/path\\?(q=hello&password=-+)|(password=-+&q=hello)')\n\n        self.assertEqual(scrubbed['GET']['q'], 'hello')\n        self.assertRegex(scrubbed['GET']['password'], r'\\*+')\n\n        self.assertEqual(scrubbed['POST']['foo'], 'bar')\n        self.assertRegex(scrubbed['POST']['confirm_password'], r'\\*+')\n        self.assertRegex(scrubbed['POST']['token'], r'\\*+')\n\n        self.assertEqual('5.6.7.8', scrubbed['headers']['X-Real-Ip'])\n\n        self.assertRegex(scrubbed['headers']['Cookies'], r'\\*+')\n        self.assertRegex(scrubbed['headers']['Authorization'], r'\\*+')\n\n    def test_filter_ip_no_user_ip(self):\n        request_data = {'something': 'but no ip'}\n        rollbar._filter_ip(request_data, False)\n        self.assertNotIn('user_ip', request_data)\n\n    def test_filter_ip_capture_true(self):\n        ip = '123.32.394.99'\n        request_data = {'user_ip': ip}\n        rollbar._filter_ip(request_data, True)\n        self.assertEqual(ip, request_data['user_ip'])\n\n    def test_filter_ip_anonymize(self):\n        ip = '123.32.394.99'\n        request_data = {'user_ip': ip}\n        rollbar._filter_ip(request_data, rollbar.ANONYMIZE)\n        self.assertNotEqual(ip, request_data['user_ip'])\n        self.assertNotEqual(None, request_data['user_ip'])\n\n    def test_filter_ip_capture_false(self):\n        ip = '123.32.394.99'\n        request_data = {'user_ip': ip}\n        rollbar._filter_ip(request_data, False)\n        self.assertNotEqual(ip, request_data['user_ip'])\n        self.assertEqual(None, request_data['user_ip'])\n\n    def test_filter_ip_ipv6_capture_false(self):\n        ip = '2607:f0d0:1002:51::4'\n        request_data = {'user_ip': ip}\n        rollbar._filter_ip(request_data, False)\n        self.assertNotEqual(ip, request_data['user_ip'])\n        self.assertEqual(None, request_data['user_ip'])\n\n    def test_filter_ip_anonymize_ipv6(self):\n        ips = [\n            'FE80:0000:0000:0000:0202:B3FF:FE1E:8329',\n            'FE80::0202:B3FF:FE1E:8329',\n            '2607:f0d0:1002:51::4',\n        ]\n        for ip in ips:\n            request_data = {'user_ip': ip}\n            rollbar._filter_ip(request_data, rollbar.ANONYMIZE)\n            self.assertNotEqual(ip, request_data['user_ip'])\n            self.assertNotEqual(None, request_data['user_ip'])\n\n    def test_starlette_extract_user_ip_from_client_host(self):\n        try:\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette package')\n\n        client_host = ('127.0.0.1', 1453)\n        ip_forwarded_for = b'192.168.10.10'\n        ip_real_ip = b'1.2.3.4'\n        scope = {\n            'type': 'http',\n            'client': client_host,\n            'headers': [\n                (b'x-forwarded-for', ip_forwarded_for),\n                (b'x-real-ip', ip_real_ip),\n            ],\n        }\n        request = Request(scope)\n\n        user_ip = rollbar._starlette_extract_user_ip(request)\n\n        self.assertEqual(user_ip, client_host[0])\n\n    def test_starlette_extract_user_ip_from_headers(self):\n        try:\n            from starlette.requests import Request\n        except ImportError:\n            self.skipTest('Requires Starlette package')\n\n        ip_forwarded_for = b'192.168.10.10'\n        ip_real_ip = b'1.2.3.4'\n\n        # Headers contain only X-Forwarded-For\n        scope = {'type': 'http', 'headers': [(b'x-forwarded-for', ip_forwarded_for)]}\n        request = Request(scope)\n        user_ip = rollbar._starlette_extract_user_ip(request)\n        self.assertEqual(user_ip, ip_forwarded_for.decode())\n\n        # Headers contain only X-Real-Ip\n        scope = {'type': 'http', 'headers': [(b'x-real-ip', ip_real_ip)]}\n        request = Request(scope)\n        user_ip = rollbar._starlette_extract_user_ip(request)\n        self.assertEqual(user_ip, ip_real_ip.decode())\n\n        # Headers contain both X-Forwarded-For and X-Real-Ip\n        scope = {\n            'type': 'http',\n            'headers': [\n                (b'x-forwarded-for', ip_forwarded_for),\n                (b'x-real-ip', ip_real_ip),\n            ],\n        }\n        request = Request(scope)\n        user_ip = rollbar._starlette_extract_user_ip(request)\n        self.assertEqual(user_ip, ip_forwarded_for.decode())\n    \n    @mock.patch('rollbar.send_payload')\n    def test_root_path(self, send_payload):\n        prev_root = rollbar.SETTINGS['root']\n        rollbar.SETTINGS['root'] = Path(\"/tmp\")\n        try:\n            called_with('original value')\n        except:\n            rollbar.report_exc_info()\n        finally:\n            rollbar.SETTINGS['root'] = prev_root\n\n        self.assertEqual(send_payload.called, True)\n\n        payload = send_payload.call_args[0][0]\n        self.assertEqual(payload['data']['server']['root'], \"/tmp\")\n\n\n### Helpers\n\ndef step1():\n    val1 = 1\n    step2()\n\n\ndef step2():\n    val2 = 2\n    raise Exception()\n\n\ndef called_with(arg1):\n    arg1 = 'changed'\n    step1()\n\n\nclass CauseException(Exception):\n    pass\n\n\nclass MockResponse:\n    def __init__(self, json_data, status_code):\n        self.json_data = json_data\n        self.status_code = status_code\n\n    @property\n    def content(self):\n        return json.dumps(self.json_data)\n\n    def json(self):\n        return self.json_data\n\n\nclass MockRawResponse:\n    def __init__(self, data, status_code):\n        self.data = data\n        self.status_code = status_code\n\n    @property\n    def content(self):\n        return self.data\n\n    def json(self):\n        return self.data\n\n\nclass MockLambdaContext(object):\n    def __init__(self, x):\n        self.function_name = 1\n        self.function_version = 2\n        self.invoked_function_arn = 3\n        self.aws_request_id = 4\n        self.x = x\n\n    def get_remaining_time_in_millis(self):\n        42\n\n\ndef fastapi_add_route_with_request_param(app, endpoint, path, request_param):\n    from fastapi import Request\n\n    endpoint.__annotations__[request_param] = Request\n\n    return app.get(path)(endpoint)\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "rollbar/test/test_scrub_redact_transform.py",
    "content": "from collections.abc import Mapping\n\nfrom rollbar.lib import transforms\nfrom rollbar.lib.transforms.scrub_redact import ScrubRedactTransform, REDACT_REF\n\nfrom rollbar.test import BaseTest\n\n\nclass NotRedactRef():\n    pass\n\nNOT_REDACT_REF = NotRedactRef()\n\ntry:\n    SCRUBBED = '*' * len(REDACT_REF)\nexcept:\n    SCRUBBED = '*' * len(str(REDACT_REF))\n\n\nclass ScrubRedactTransformTest(BaseTest):\n    def _assertScrubbed(self, start, expected, redact_char='*', skip_id_check=False):\n        scrubber = ScrubRedactTransform(redact_char=redact_char, randomize_len=False)\n        result = transforms.transform(start, scrubber)\n\n        if not skip_id_check:\n            self.assertNotEqual(id(result), id(expected))\n\n        self.assertEqual(type(result), type(expected))\n\n        if isinstance(result, Mapping):\n            self.assertDictEqual(result, expected)\n        elif isinstance(result, tuple):\n            self.assertTupleEqual(result, expected)\n        elif isinstance(result, list):\n            self.assertListEqual(result, expected)\n        elif isinstance(result, set):\n            self.assertSetEqual(result, expected)\n        else:\n            self.assertEqual(result, expected)\n\n    def test_no_scrub(self):\n        obj = NOT_REDACT_REF\n        expected = NOT_REDACT_REF\n        self._assertScrubbed(obj, expected, skip_id_check=True)\n\n    def test_scrub(self):\n        obj = REDACT_REF\n        expected = SCRUBBED\n        self._assertScrubbed(obj, expected)\n\n    def test_scrub_list(self):\n        obj = [REDACT_REF, REDACT_REF, REDACT_REF]\n        expected = [SCRUBBED, SCRUBBED, SCRUBBED]\n        self._assertScrubbed(obj, expected)\n\n    def test_scrub_set(self):\n        obj = set([REDACT_REF, NOT_REDACT_REF])\n        expected = set([SCRUBBED, NOT_REDACT_REF])\n        self._assertScrubbed(obj, expected)\n\n    def test_scrub_tuple(self):\n        obj = (REDACT_REF, REDACT_REF, REDACT_REF)\n        expected = (SCRUBBED, SCRUBBED, SCRUBBED)\n        self._assertScrubbed(obj, expected)\n\n    def test_scrub_namedtuple(self):\n        from collections import namedtuple\n        TestNamedTuple = namedtuple('TestNamedTuple', ['scrub_me', 'dont_scrub_me'])\n        obj = TestNamedTuple(scrub_me=REDACT_REF, dont_scrub_me=NOT_REDACT_REF)\n        expected = TestNamedTuple(scrub_me=SCRUBBED, dont_scrub_me=NOT_REDACT_REF)\n        self._assertScrubbed(obj, expected)\n\n    def test_scrub_dict(self):\n        obj = {'scrub_me': REDACT_REF, 'dont_scrub_me': NOT_REDACT_REF}\n        expected = {'scrub_me': SCRUBBED, 'dont_scrub_me': NOT_REDACT_REF}\n        self._assertScrubbed(obj, expected)\n"
  },
  {
    "path": "rollbar/test/test_scrub_transform.py",
    "content": "import copy\n\nfrom collections.abc import Mapping\n\nfrom rollbar.lib import transforms\nfrom rollbar.lib.transforms.scrub import ScrubTransform\n\nfrom rollbar.test import BaseTest, SNOWMAN\n\n\nclass ScrubTransformTest(BaseTest):\n    def _assertScrubbed(self, suffixes, start, expected, redact_char='*', skip_id_check=False):\n        scrubber = ScrubTransform(suffixes=suffixes, redact_char=redact_char, randomize_len=False)\n        result = transforms.transform(start, scrubber)\n\n        \"\"\"\n        print start\n        print result\n        print expected\n        \"\"\"\n\n        if not skip_id_check:\n            self.assertNotEqual(id(result), id(expected))\n\n        self.assertEqual(type(result), type(expected))\n\n        if isinstance(result, Mapping):\n            self.assertDictEqual(result, expected)\n        elif isinstance(result, tuple):\n            self.assertTupleEqual(result, expected)\n        elif isinstance(result, (list, set)):\n            self.assertListEqual(result, expected)\n        else:\n            self.assertEqual(result, expected)\n\n    def test_string(self):\n        obj = 'simple_string'\n\n        # The Python interpreter is pretty smart... let's fool it into\n        # not using the same string object for the expected var\n        expected = ('simple_string' + ' dummy').split()[0]\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_num(self):\n        obj = 22\n        expected = 22\n        self._assertScrubbed([['password']], obj, expected, skip_id_check=True)\n\n    def test_None(self):\n        obj = None\n        expected = None\n        self._assertScrubbed([['password']], obj, expected, skip_id_check=True)\n\n    def test_list_of_None(self):\n        obj = [None, None, None]\n        expected = [None, None, None]\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_no_suffixes(self):\n        obj = {'hello': 'world', 'password': 'cleartext'}\n        expected = dict(obj)\n        self._assertScrubbed([], obj, expected)\n\n    def test_scrub_named_tuple(self):\n        from collections import namedtuple\n        Obj = namedtuple('obj', ['hello', 'password'])\n        obj = Obj('world', 'cleartext')\n        expected = Obj('world', '*********')\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_named_tuple_in_list(self):\n        from collections import namedtuple\n        Obj = namedtuple('obj', ['hello', 'password'])\n        obj = [Obj('world', 'cleartext'), 'another element']\n        expected = [Obj('world', '*********'), 'another element']\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_simple_dict(self):\n        obj = {'hello': 'world', 'password': 'cleartext'}\n        expected = copy.deepcopy(obj)\n        expected['password'] = '*********'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_dict_in_list(self):\n        obj = [{'hello': 'world', 'password': 'cleartext'}, 'another element']\n        expected = copy.deepcopy(obj)\n        expected[0]['password'] = '*********'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_dict_in_tuple(self):\n        obj = ({'hello': 'world', 'password': 'cleartext'}, 'another element')\n        expected = copy.deepcopy(obj)\n        expected[0]['password'] = '*********'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_dict_in_dict(self):\n        obj = {'the data': {'hello': 'world', 'password': 'cleartext'}, 'some other data': 3}\n        expected = copy.deepcopy(obj)\n        expected['the data']['password'] = '*********'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_multiple_suffixes(self):\n        obj = {'hello': 'world', 'password': 'cleartext'}\n        expected = copy.deepcopy(obj)\n        expected['hello'] = '*****'\n        expected['password'] = '*********'\n        self._assertScrubbed([['hello'], ['password']], obj, expected)\n\n    def test_scrub_no_suffix_match(self):\n        obj = {'hello': 'world', 'password': 'cleartext'}\n        expected = copy.deepcopy(obj)\n        self._assertScrubbed([['not found'], ['access_token']], obj, expected)\n\n    def test_scrub_full_dict(self):\n        obj = {'hello': 'world', 'password': {'nested': 'secrets'}}\n        expected = copy.deepcopy(obj)\n        expected['password'] = '*'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_full_list(self):\n        obj = {'hello': 'world', 'password': [1, 2, 3, 4]}\n        expected = copy.deepcopy(obj)\n        expected['password'] = '****'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_full_dict_with_nested_secret(self):\n        obj = {'hello': 'world', 'password': [{'password': 'secret', 'nested': {'password': 'secret!!!'}}]}\n        expected = copy.deepcopy(obj)\n        expected['password'] = '*'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_one_secret_and_one_clear_reference(self):\n        ref = {'scrub': 'me', 'some': 'times'}\n        obj = {'hello': 'world', 'password': ref, 'should be clear': ref}\n        expected = copy.deepcopy(obj)\n        expected['password'] = '**'\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_scrub_circular(self):\n        ref = {'scrub': 'me', 'some': 'times'}\n        obj = {'hello': 'world', 'password': ref}\n        ref['circular'] = obj\n        \"\"\"\n        Obj A =\n        {\n          'hello': 'world',\n          'password': {\n            'scrub': 'me', 'some': 'times', 'circular': A\n          }\n        }\n        \"\"\"\n        expected = copy.deepcopy(obj)\n        expected['password'] = '***'\n\n        self._assertScrubbed([['password']], obj, expected)\n\n    def test_circular(self):\n        ref = {'scrub': 'me', 'some': 'times'}\n        obj = {'hello': 'world', 'password': ref}\n        ref['circular'] = obj\n\n        scrubber = ScrubTransform([])\n        result = transforms.transform(obj, scrubber)\n\n        self.assertIsNot(result, obj)\n        self.assertIsNot(result['password'], ref)\n        self.assertIsNot(result['password']['circular'], obj)\n        self.assertIs(result['password']['circular']['password'], result['password']['circular']['password']['circular']['password'])\n\n    def test_unicode_keys(self):\n        obj = {\n            SNOWMAN: 'hello'\n        }\n        expected = copy.deepcopy(obj)\n        self._assertScrubbed([], obj, expected)\n\n    def test_scrub_unicode_keys(self):\n        obj = {\n            SNOWMAN: 'hello'\n        }\n        expected = {\n            SNOWMAN: '*****'\n        }\n        self._assertScrubbed([[SNOWMAN]], obj, expected)\n\n    def test_scrub_unicode_values(self):\n        obj = {\n            'password': SNOWMAN\n        }\n        expected = {\n            'password': '***'\n        }\n        self._assertScrubbed([['password']], obj, expected)\n"
  },
  {
    "path": "rollbar/test/test_scruburl_transform.py",
    "content": "from urllib.parse import urlparse, parse_qs\n\nfrom rollbar.lib import transforms, string_types\nfrom rollbar.lib.transforms.scruburl import ScrubUrlTransform, _starts_with_auth_re\n\nfrom rollbar.test import BaseTest, SNOWMAN_UNICODE\n\n\nSNOWMAN_LEN = len(SNOWMAN_UNICODE)\n\n\nclass ScrubUrlTransformTest(BaseTest):\n    def _assertScrubbed(self,\n                        params_to_scrub,\n                        start,\n                        expected,\n                        scrub_username=False,\n                        scrub_password=True,\n                        redact_char='-',\n                        skip_id_check=False):\n        scrubber = ScrubUrlTransform(suffixes=[],\n                                     params_to_scrub=params_to_scrub,\n                                     scrub_username=scrub_username,\n                                     scrub_password=scrub_password,\n                                     redact_char=redact_char,\n                                     randomize_len=False)\n        result = transforms.transform(start, scrubber)\n\n        if not skip_id_check:\n            self.assertNotEqual(id(result), id(expected))\n\n        self.assertEqual(type(expected), type(result))\n        self.assertIsInstance(result, string_types)\n        self._compare_urls(expected, result)\n\n    def _compare_urls(self, url1, url2):\n        if _starts_with_auth_re.match(url1):\n            url1 = '//%s' % url1\n\n        if _starts_with_auth_re.match(url2):\n            url2 = '//%s' % url2\n\n        parsed_urls = [urlparse(url) for url in (url1, url2)]\n        qs_params = [parse_qs(x.query, keep_blank_values=True) for x in parsed_urls]\n        num_params = [len(x) for x in qs_params]\n        param_names = [set(x.keys()) for x in qs_params]\n\n        self.assertEqual(*num_params)\n        self.assertDictEqual(*qs_params)\n        self.assertSetEqual(*param_names)\n\n        for facet in ('scheme', 'netloc', 'path', 'params', 'username', 'password', 'hostname', 'port'):\n            comp = [getattr(x, facet) for x in parsed_urls]\n            self.assertEqual(*comp)\n\n    def test_no_scrub(self):\n        obj = 'http://hello.com/?foo=bar'\n        expected = obj\n        self._assertScrubbed(['password'], obj, expected, skip_id_check=True)\n\n    def test_not_url(self):\n        obj = 'I am a plain\\'ol string'\n        expected = obj\n        self._assertScrubbed(['password'], obj, expected, skip_id_check=True)\n\n    def test_scrub_simple_url_params(self):\n        obj = 'http://foo.com/asdf?password=secret'\n        expected = obj.replace('secret', '------')\n        self._assertScrubbed(['password'], obj, expected)\n\n    def test_scrub_utf8_url_params(self):\n        obj = 'http://foo.com/asdf?password=%s' % SNOWMAN_UNICODE\n        expected = obj.replace(SNOWMAN_UNICODE, '-' * SNOWMAN_LEN)\n        self._assertScrubbed(['password'], obj, expected)\n\n    def test_scrub_utf8_url_keys(self):\n        obj = 'http://foo.com/asdf?%s=secret' % SNOWMAN_UNICODE\n        expected = obj.replace('secret', '------')\n        self._assertScrubbed([str(SNOWMAN_UNICODE)], obj, expected)\n\n    def test_scrub_multi_url_params(self):\n        obj = 'http://foo.com/asdf?password=secret&password=secret2&token=TOK&clear=text'\n        expected = obj.replace('secret2', '-------').replace('secret', '------').replace('TOK', '---')\n        self._assertScrubbed(['password', 'token'], obj, expected)\n\n    def test_scrub_password_auth(self):\n        obj = 'http://cory:secr3t@foo.com/asdf?password=secret&clear=text'\n        expected = obj.replace('secr3t', '------').replace('secret', '------')\n        self._assertScrubbed(['password'], obj, expected)\n\n    def test_scrub_username_auth(self):\n        obj = 'http://cory:secr3t@foo.com/asdf?password=secret&clear=text'\n        expected = obj.replace('cory', '----').replace('secret', '------')\n        self._assertScrubbed(['password'], obj, expected, scrub_password=False, scrub_username=True)\n\n    def test_scrub_username_and_password_auth(self):\n        obj = 'http://cory:secr3t@foo.com/asdf?password=secret&clear=text'\n        expected = obj.replace('cory', '----').replace('secr3t', '------').replace('secret', '------')\n        self._assertScrubbed(['password'], obj, expected, scrub_password=True, scrub_username=True)\n\n    def test_scrub_missing_scheme(self):\n        obj = '//cory:secr3t@foo.com/asdf?password=secret&clear=text'\n        expected = obj.replace('secr3t', '------').replace('secret', '------')\n        self._assertScrubbed(['password'], obj, expected)\n\n    def test_scrub_missing_scheme_and_double_slash(self):\n        obj = 'cory:secr3t@foo.com/asdf?password=secret&clear=text'\n        expected = obj.replace('secr3t', '------').replace('secret', '------')\n        self._assertScrubbed(['password'], obj, expected)\n\n    def test_keep_blank_url_params(self):\n        obj = 'http://foo.com/asdf?foo=bar&baz='\n        expected = obj\n        self._assertScrubbed(['password'], obj, expected, skip_id_check=True)\n\n    def test_scrub_dict_val_isnt_string(self):\n\n        url = 'cory:secr3t@foo.com/asdf?password=secret&clear=text'\n\n        # Every string which is a URL should be scrubbed\n        obj = {\n            'url': [url]\n        }\n\n        scrubber = ScrubUrlTransform(suffixes=[('url',)], params_to_scrub=['password'], randomize_len=False)\n        result = transforms.transform(obj, scrubber)\n\n        expected = url.replace('secr3t', '------').replace('secret', '------')\n        self._assertScrubbed(['password'], result['url'][0], expected)\n\n    def test_scrub_dict_nested_key_match_with_circular_ref(self):\n        # If a URL is a circular reference then let's make sure to\n        # show the scrubbed, original URL\n        url = 'cory:secr3t@foo.com/asdf?password=secret&clear=text'\n        obj = {\n            'url': [{'link': url}],\n            'link': [{'url': url}]\n        }\n\n        scrubber = ScrubUrlTransform(suffixes=[('url',), ('link',)], params_to_scrub=['password'], randomize_len=False)\n        result = transforms.transform(obj, scrubber)\n\n        self.assertNotIn('secr3t', result['url'][0]['link'])\n        self.assertNotIn('secret', result['url'][0]['link'])\n        self.assertNotIn('secr3t', result['link'][0]['url'])\n        self.assertNotIn('secret', result['link'][0]['url'])\n        self.assertNotRegex(result['url'][0]['link'], r'^-+$')\n        self.assertNotRegex(result['link'][0]['url'], r'^-+$')\n"
  },
  {
    "path": "rollbar/test/test_serializable_transform.py",
    "content": "import collections\nimport base64\nimport copy\nimport enum\nimport sys\n\nfrom collections.abc import Mapping\n\nfrom rollbar.lib import transforms\nfrom rollbar.lib.transforms.serializable import SerializableTransform\n\nfrom rollbar.test import BaseTest, SNOWMAN_UNICODE\n\nSNOWMAN_LEN = len(SNOWMAN_UNICODE)\n\n\n# This base64 encoded string contains bytes that do not\n# convert to utf-8 data\ninvalid_b64 = b'CuX2JKuXuLVtJ6l1s7DeeQ=='\n\ninvalid = base64.b64decode(invalid_b64)\nundecodable_repr = f'<Undecodable type:({\"bytes\"}) base64:({invalid_b64.decode(\"ascii\")})>'\n\n\nclass SerializableTransformTest(BaseTest):\n    def _assertSerialized(self, start, expected, safe_repr=True, safelist=None, skip_id_check=False):\n        serializable = SerializableTransform(safe_repr=safe_repr, safelist_types=safelist)\n        result = transforms.transform(start, serializable)\n\n        \"\"\"\n        #print start\n        print result\n        print expected\n        \"\"\"\n\n        if not skip_id_check:\n            self.assertNotEqual(id(result), id(expected))\n\n        self.assertEqual(type(expected), type(result))\n\n        if isinstance(result, Mapping):\n            self.assertDictEqual(result, expected)\n        elif isinstance(result, tuple):\n            self.assertTupleEqual(result, expected)\n        elif isinstance(result, (list, set)):\n            self.assertListEqual(result, expected)\n        else:\n            self.assertEqual(result, expected)\n\n    def test_simple_dict(self):\n        start = {\n            'hello': 'world',\n            '1': 2,\n        }\n        expected = copy.deepcopy(start)\n        self._assertSerialized(start, expected)\n\n    def test_enum(self):\n        class EnumTest(enum.Enum):\n            one = 1\n            two = 2\n\n        start = EnumTest.one\n        expected = \"<enum 'EnumTest'>\"\n        self._assertSerialized(start, expected)\n\n    def test_enum_no_safe_repr(self):\n        class EnumTest(enum.Enum):\n            one = 1\n            two = 2\n\n        start = EnumTest.one\n        expected = '<EnumTest.one: 1>'\n        self._assertSerialized(start, expected, safe_repr=False)\n\n    def test_int_enum(self):\n        class EnumTest(enum.IntEnum):\n            one = 1\n            two = 2\n\n        start = EnumTest.one\n        expected = \"<enum 'EnumTest'>\"\n        self._assertSerialized(start, expected)\n\n    def test_int_enum_no_safe_repr(self):\n        class EnumTest(enum.IntEnum):\n            one = 1\n            two = 2\n\n        start = EnumTest.one\n        expected = '<EnumTest.one: 1>'\n        self._assertSerialized(start, expected, safe_repr=False)\n\n    def test_encode_dict_with_invalid_utf8(self):\n        start = {\n            'invalid': invalid\n        }\n        expected = copy.copy(start)\n        expected['invalid'] = undecodable_repr\n        self._assertSerialized(start, expected)\n\n    def test_encode_utf8(self):\n        start = invalid\n        expected = undecodable_repr\n        self._assertSerialized(start, expected)\n\n    def test_encode_None(self):\n        start = None\n        expected = None\n        self._assertSerialized(start, expected, skip_id_check=True)\n\n    def test_encode_float(self):\n        start = 3.14\n        expected = 3.14\n        self._assertSerialized(start, expected, skip_id_check=True)\n\n    def test_encode_float_nan(self):\n        start = float('nan')\n        expected = '<NaN>'\n        self._assertSerialized(start, expected, skip_id_check=True)\n\n    def test_encode_float_infinity(self):\n        start = float('inf')\n        expected = '<Infinity>'\n        self._assertSerialized(start, expected, skip_id_check=True)\n\n    def test_encode_float_neg_infinity(self):\n        start = float('-inf')\n        expected = '<NegativeInfinity>'\n        self._assertSerialized(start, expected, skip_id_check=True)\n\n    def test_encode_int(self):\n        start = 33\n        expected = 33\n        self._assertSerialized(start, expected, skip_id_check=True)\n\n    def test_encode_empty_tuple(self):\n        start = ()\n        expected = ()\n        \n        skip_id_check = False\n        # different behavior in 3.11\n        if sys.version_info >= (3, 11):\n            skip_id_check = True\n        \n        self._assertSerialized(start, expected, skip_id_check=skip_id_check)\n\n    def test_encode_empty_list(self):\n        start = []\n        expected = []\n        self._assertSerialized(start, expected)\n\n    def test_encode_empty_dict(self):\n        start = {}\n        expected = {}\n        self._assertSerialized(start, expected)\n\n    def test_encode_namedtuple(self):\n        MyType = collections.namedtuple('MyType', ('field_1', 'field_2'))\n        nt = MyType(field_1='this is field 1', field_2=invalid)\n\n        start = nt\n        expected = \"<MyType(field_1='this is field 1', field_2='%s')>\" % undecodable_repr\n\n        self._assertSerialized(start, expected)\n\n    def test_encode_tuple_with_bytes(self):\n        start = ('hello', 'world', invalid)\n        expected = list(start)\n        expected[2] = undecodable_repr\n        self._assertSerialized(start, tuple(expected))\n\n    def test_encode_list_with_bytes(self):\n        start = ['hello', 'world', invalid]\n        expected = list(start)\n        expected[2] = undecodable_repr\n        self._assertSerialized(start, expected)\n\n    def test_encode_dict_with_bytes(self):\n        start = {'hello': 'world', 'invalid': invalid}\n        expected = copy.deepcopy(start)\n        expected['invalid'] = undecodable_repr\n        self._assertSerialized(start, expected)\n\n    def test_encode_dict_with_bytes_key(self):\n        start = {'hello': 'world', invalid: 'works?'}\n        expected = copy.deepcopy(start)\n        expected[undecodable_repr] = 'works?'\n        del expected[invalid]\n        self._assertSerialized(start, expected)\n\n    def test_encode_with_rollbar_repr(self):\n        class CustomRepr(object):\n            def __rollbar_repr__(self):\n                return 'hello'\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n        expected = copy.deepcopy(start)\n        expected['custom'] = 'hello'\n        self._assertSerialized(start, expected)\n\n    def test_encode_with_custom_repr_no_safelist(self):\n        class CustomRepr(object):\n            def __repr__(self):\n                return 'hello'\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n        expected = copy.deepcopy(start)\n        expected['custom'] = str(CustomRepr)\n        self._assertSerialized(start, expected)\n\n    def test_encode_with_custom_repr_no_safelist_no_safe_repr(self):\n        class CustomRepr(object):\n            def __repr__(self):\n                return 'hello'\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n        expected = copy.deepcopy(start)\n        expected['custom'] = 'hello'\n        self._assertSerialized(start, expected, safe_repr=False)\n\n    def test_encode_with_custom_repr_safelist(self):\n        class CustomRepr(object):\n            def __repr__(self):\n                return 'hello'\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n        expected = copy.deepcopy(start)\n        expected['custom'] = 'hello'\n        self._assertSerialized(start, expected, safelist=[CustomRepr])\n\n    def test_encode_with_custom_repr_returns_bytes(self):\n        class CustomRepr(object):\n            def __repr__(self):\n                return b'hello'\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n\n        serializable = SerializableTransform(safelist_types=[CustomRepr])\n        result = transforms.transform(start, serializable)\n\n        self.assertRegex(result['custom'], \"<class '.*CustomRepr'>\")\n\n    def test_encode_with_custom_repr_returns_object(self):\n        class CustomRepr(object):\n            def __repr__(self):\n                return {'hi': 'there'}\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n\n        serializable = SerializableTransform(safelist_types=[CustomRepr])\n        result = transforms.transform(start, serializable)\n        self.assertRegex(result['custom'], \"<class '.*CustomRepr'>\")\n\n    def test_encode_with_custom_repr_returns_unicode(self):\n        class CustomRepr(object):\n            def __repr__(self):\n                return SNOWMAN_UNICODE\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n        expected = copy.deepcopy(start)\n        expected['custom'] = SNOWMAN_UNICODE\n        self._assertSerialized(start, expected, safelist=[CustomRepr])\n\n    def test_encode_with_bad_repr_doesnt_die(self):\n        class CustomRepr(object):\n            def __repr__(self):\n                assert False\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n        serializable = SerializableTransform(safelist_types=[CustomRepr])\n        result = transforms.transform(start, serializable)\n        self.assertRegex(result['custom'], \"<AssertionError.*CustomRepr.*>\")\n\n    def test_encode_with_bad_str_doesnt_die(self):\n\n        class UnStringableException(Exception):\n            def __str__(self):\n                raise Exception('asdf')\n\n        class CustomRepr(object):\n\n            def __repr__(self):\n                raise UnStringableException()\n\n        start = {'hello': 'world', 'custom': CustomRepr()}\n        serializable = SerializableTransform(safelist_types=[CustomRepr])\n        result = transforms.transform(start, serializable)\n        self.assertRegex(result['custom'], \"<UnStringableException.*Exception.*str.*>\")\n"
  },
  {
    "path": "rollbar/test/test_session.py",
    "content": "import asyncio\nimport threading\nimport unittest\n\nfrom rollbar.test import BaseTest\nfrom rollbar.lib import session\n\n\nclass SessionTest(BaseTest):\n    def test_session_threading(self):\n        results = []\n\n        def worker(headers):\n            session.set_current_session(headers)\n            results.append(session.get_current_session())\n\n        t1 = threading.Thread(target=worker, args=({\n            'Baggage': 'rollbar.session.id=abc123,rollbar.execution.scope.id=123abc'\n        },))\n        t2 = threading.Thread(target=worker, args=({\n            'Baggage': 'rollbar.session.id=def456,rollbar.execution.scope.id=456def'\n        },))\n        t3 = threading.Thread(target=worker, args=({},))\n\n        t1.start()\n        t2.start()\n        t3.start()\n        t1.join()\n        t2.join()\n        t3.join()\n\n        self.assertEqual(len(results), 3)\n        self.assertEqual(results[0], [\n            {'key': 'session_id', 'value': 'abc123'},\n            {'key': 'execution_scope_id', 'value': '123abc'},\n        ])\n        self.assertEqual(results[1], [\n            {'key': 'session_id', 'value': 'def456'},\n            {'key': 'execution_scope_id', 'value': '456def'},\n        ])\n        # For the thread with empty headers, we should still get a session ID generated.\n        self.assertEqual(results[2][0]['key'], 'execution_scope_id')\n        self.assertEqual(len(results[2][0]['value']), 32)\n\n    def test_parse_session_request_baggage_headers(self):\n        headers = {\n            'Baggage': 'rollbar.session.id=abc123, rollbar.execution.scope.id=def456',\n        }\n        attributes = session.parse_session_request_baggage_headers(headers)\n        self.assertEqual([\n            {'key': 'session_id', 'value': 'abc123'},\n            {'key': 'execution_scope_id', 'value': 'def456'},\n        ], attributes)\n\n    def test_parse_session_request_baggage_headers_lower(self):\n        headers = {\n            'baggage': 'rollbar.execution.scope.id=abc123',\n        }\n        attributes = session.parse_session_request_baggage_headers(headers)\n        self.assertEqual([\n            {'key': 'execution_scope_id', 'value': 'abc123'},\n        ], attributes)\n\n    def test_parse_session_request_baggage_headers_scope_only(self):\n        headers = {\n            'baggage': 'rollbar.execution.scope.id=def456',\n        }\n        attributes = session.parse_session_request_baggage_headers(headers)\n        self.assertEqual([\n            {'key': 'execution_scope_id', 'value': 'def456'},\n        ], attributes)\n\n    def test_parse_session_request_baggage_headers_empty(self):\n        headers = {\n            'baggage': '',\n        }\n        attributes = session.parse_session_request_baggage_headers(headers)\n        self.assertEqual(len(attributes), 0)\n\n    def test_parse_session_request_baggage_headers_empty_generate(self):\n        headers = {\n            'baggage': '',\n        }\n        attributes = session.parse_session_request_baggage_headers(headers, generate_missing=True)\n        # Ensure that we still generate an execution scope ID if the baggage header is empty.\n        self.assertEqual(len(attributes), 1)\n        self.assertEqual(attributes[0]['key'], 'execution_scope_id')\n        self.assertEqual(len(attributes[0]['value']), 32)\n\n    def test_parse_session_request_baggage_headers_other(self):\n        headers = {\n            'baggage': 'some.id=xyz789',\n        }\n        attributes = session.parse_session_request_baggage_headers(headers)\n        self.assertEqual(len(attributes), 0)\n\n    def test_parse_session_request_baggage_headers_other_generate(self):\n        headers = {\n            'baggage': 'some.id=xyz789',\n        }\n        attributes = session.parse_session_request_baggage_headers(headers, generate_missing=True)\n        # Ensure that we still generate an execution scope ID if the baggage header doesn't contain the expected keys.\n        self.assertEqual(len(attributes), 1)\n        self.assertEqual(attributes[0]['key'], 'execution_scope_id')\n        self.assertEqual(len(attributes[0]['value']), 32)\n\n    def test_build_new_session_attributes(self):\n        attributes = session._build_new_scope_attributes()\n        self.assertEqual(len(attributes), 1)\n        self.assertEqual(attributes[0]['key'], 'execution_scope_id')\n        self.assertEqual(len(attributes[0]['value']), 32)\n\n    def test_new_session_id(self):\n        session_id = session._new_scope_id()\n        self.assertEqual(len(session_id), 32)\n\n\nclass TestSessionAsync(unittest.IsolatedAsyncioTestCase):\n    \"\"\"\n    Test that session data is properly isolated across async tasks.\n    \"\"\"\n\n    async def test_session_async(self):\n        results = {}\n\n        async def worker(headers, key):\n            session.set_current_session(headers)\n            await asyncio.sleep(0.1) # Force a context switch to test async isolation\n            results[key] = session.get_current_session()\n\n        await asyncio.gather(\n            worker({'Baggage': 'rollbar.session.id=abc123,rollbar.execution.scope.id=123abc'}, 'task1'),\n            worker({'Baggage': 'rollbar.session.id=def456,rollbar.execution.scope.id=456def'}, 'task2'),\n            worker({}, 'task3'),\n        )\n\n        self.assertEqual(results['task1'], [\n            {'key': 'session_id', 'value': 'abc123'},\n            {'key': 'execution_scope_id', 'value': '123abc'},\n        ])\n        self.assertEqual(results['task2'], [\n            {'key': 'session_id', 'value': 'def456'},\n            {'key': 'execution_scope_id', 'value': '456def'},\n        ])\n        # For the task with empty headers, we should still get a session ID generated.\n        self.assertEqual(results['task3'][0]['key'], 'execution_scope_id')\n        self.assertEqual(len(results['task3'][0]['value']), 32)\n"
  },
  {
    "path": "rollbar/test/test_shortener_transform.py",
    "content": "import sys\nfrom array import array\nfrom collections import deque\n\nfrom rollbar import DEFAULT_LOCALS_SIZES, SETTINGS\nfrom rollbar.lib import transforms\nfrom rollbar.lib.transforms.shortener import ShortenerTransform\nfrom rollbar.test import BaseTest\n\n\nclass TestClassWithAVeryVeryVeryVeryVeryVeryVeryLongName:\n    pass\n\n\nclass KeyMemShortenerTransform(ShortenerTransform):\n    \"\"\"\n    A shortener that just stores the keys.\n    \"\"\"\n    keysUsed = []\n\n    def default(self, o, key=None):\n        self.keysUsed.append((key, o))\n        return super(KeyMemShortenerTransform, self).default(o, key=key)\n\n\nclass ShortenerTransformTest(BaseTest):\n    def setUp(self):\n        self.shortener = ShortenerTransform(keys=[('shorten',)], **DEFAULT_LOCALS_SIZES)\n\n    def test_shorten_string(self):\n        original = 'x' * 120\n        shortened = '{}...{}'.format('x'*48, 'x'*49)\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_long(self):\n        original = 17955682733916468498414734863645002504519623752387\n        shortened = '179556827339164684...5002504519623752387'\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_mapping(self):\n        original = {\n                'one': 'one',\n                'two': 'two',\n                'three': 'three',\n                'four': 'four',\n                'five': 'five',\n                'six': 'six',\n                'seven': 'seven',\n                'eight': 'eight',\n                'nine': 'nine',\n                'ten': 'ten',\n                'eleven': 'eleven'\n            }\n        shortened = {\n                'one': 'one',\n                'two': 'two',\n                'three': 'three',\n                'four': 'four',\n                'five': 'five',\n                'six': 'six',\n                'seven': 'seven',\n                'eight': 'eight',\n                'nine': 'nine',\n                'ten': 'ten',\n            }\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_bytes(self):\n        original = b'\\x78' * 120\n        shortened = b'\\x78' * 100\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_list(self):\n        original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n        shortened = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '...']\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_tuple(self):\n        original = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)\n        shortened = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '...')\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_set(self):\n        original = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}\n        shortened = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '...'}\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_frozenset(self):\n        original = frozenset([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])\n        shortened = frozenset([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '...'])\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_array(self):\n        original = array('l', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])\n        shortened = array('l', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_deque(self):\n        original = deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 15)\n        shortened = deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 15)\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_other(self):\n        original = TestClassWithAVeryVeryVeryVeryVeryVeryVeryLongName()\n        shortened = '<rollbar.test.test_shortener_transform.TestClas...'\n        self.assertIn(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_object(self):\n        data = {'request': {'POST': {i: i for i in range(12)}}}\n        keys = [\n                ('request', 'POST'),\n                ('request', 'json'),\n                ('body', 'request', 'POST'),\n                ('body', 'request', 'json'),\n                ]\n        self.assertEqual(len(data['request']['POST']), 12)\n        shortener = ShortenerTransform(keys=keys, **DEFAULT_LOCALS_SIZES)\n        result = transforms.transform(data, shortener)\n        self.assertEqual(type(result), dict)\n        self.assertEqual(len(result['request']['POST']), 10)\n\n    def test_shorten_custom_rollbar_repr(self):\n        class CustomObj:\n            value = 'value'\n            def __rollbar_repr__(self):\n                return f'<custom: {self.value}>'\n\n        obj = CustomObj()\n\n        original = obj\n        shortened = '<custom: value>'\n        self.assertEqual(shortened, self.shortener.default(original, ('shorten',)))\n        self.assertEqual(original, self.shortener.default(original, ('nope',)))\n\n    def test_shorten_frame(self):\n        data = {\n            'body': {\n                'trace': {\n                    'frames': [\n                        {\n                            \"filename\": \"/path/to/app.py\",\n                            \"lineno\": 82,\n                            \"method\": \"sub_func\",\n                            \"code\": \"extra(**kwargs)\",\n                            \"keywordspec\": \"kwargs\",\n                            \"locals\": {\n                                \"kwargs\": {\n                                    \"app\": [\"foo\", \"bar\", \"baz\", \"qux\", \"quux\", \"corge\", \"grault\", \"garply\", \"waldo\",\n                                            \"fred\", \"plugh\", \"xyzzy\", \"thud\"],\n                                    \"extra\": {\n                                        \"request\": \"<class 'some.package.MyClass'>\"\n                                    }\n                                },\n                                \"one\": {\n                                    \"two\": {\n                                        \"three\": {\n                                            \"four\": {\n                                                \"five\": {\n                                                    \"six\": {\n                                                        \"seven\": 8,\n                                                        \"eight\": \"nine\"\n                                                    },\n                                                    \"ten\": \"Yep! this should still be here, but it is a little on the \"\n                                                           \"long side, so we might want to cut it down a bit.\"\n                                                }\n                                            }\n                                        }\n                                    },\n                                    \"a\": [\"foo\", \"bar\", \"baz\", \"qux\", 5, 6, 7, 8, 9, 10, 11, 12],\n                                    \"b\": 14071504106566481658450568387453168916351054663,\n                                    \"app_id\": 140715046161904,\n                                    \"bar\": \"im a bar\",\n                                }\n                            }\n                        }\n                    ]\n                }\n            }\n        }\n        keys = [('body', 'trace', 'frames', '*', 'locals', '*')]\n        shortener = ShortenerTransform(keys=keys, **DEFAULT_LOCALS_SIZES)\n        result = transforms.transform(data, shortener)\n        expected = {\n            'body': {\n                'trace': {\n                    'frames': [\n                        {\n                            \"filename\": \"/path/to/app.py\",\n                            \"lineno\": 82,\n                            \"method\": \"sub_func\",\n                            \"code\": \"extra(**kwargs)\",\n                            \"keywordspec\": \"kwargs\",\n                            \"locals\": {\n                                \"kwargs\": {\n                                    # Shortened\n                                    \"app\": ['foo', 'bar', 'baz', 'qux', 'quux', 'corge', 'grault', 'garply', 'waldo',\n                                           'fred', '...'],\n                                    \"extra\": {\n                                        \"request\": \"<class 'some.package.MyClass'>\"\n                                    }\n                                },\n                                \"one\": {\n                                    \"two\": {\n                                        \"three\": {\n                                            \"four\": {\n                                                \"five\": {\n                                                    \"six\": {'...': '...'},  # Dropped because it is past the maxlevel.\n                                                    # Shortened\n                                                    \"ten\": \"Yep! this should still be here, but it is a litt...long \"\n                                                           \"side, so we might want to cut it down a bit.\"\n                                                }\n                                            }\n                                        }\n                                    },\n                                    \"a\": ['foo', 'bar', 'baz', 'qux', 5, 6, 7, 8, 9, 10, '...'],   # Shortened\n                                    \"b\": '140715041065664816...7453168916351054663',  # Shortened\n                                    \"app_id\": 140715046161904,\n                                    \"bar\": \"im a bar\",\n                                }\n                            }\n                        }\n                    ]\n                }\n            }\n        }\n\n        self.assertEqual(result, expected)\n\n    def test_breadth_first(self):\n        obj = {\n            \"one\": [\"four\", \"five\", 6, 7],\n            \"two\": (\"eight\", \"nine\", \"ten\"),\n            \"three\": {\n                \"eleven\": 12,\n                \"thirteen\": 14\n            }\n        }\n\n        shortener_instance = KeyMemShortenerTransform(\n            safe_repr=True,\n            keys=[\n                ('request', 'POST'),\n                ('request', 'json'),\n                ('body', 'request', 'POST'),\n                ('body', 'request', 'json'),\n            ],\n            **SETTINGS['locals']['sizes']\n        )\n\n        transforms.transform(obj, [shortener_instance], key=())\n\n        self.assertEqual(\n            shortener_instance.keysUsed,\n            [\n                ((), {\n                    \"one\": [\"four\", \"five\", 6, 7],\n                    \"two\": (\"eight\", \"nine\", \"ten\"),\n                    \"three\": {\n                        \"eleven\": 12,\n                        \"thirteen\": 14\n                    }\n                }),\n                ((\"one\",), [\"four\", \"five\", 6, 7]),\n                ((\"one\", 0), \"four\"),\n                ((\"one\", 1), \"five\"),\n                ((\"one\", 2), 6),\n                ((\"one\", 3), 7),\n                ((\"two\",), (\"eight\", \"nine\", \"ten\")),\n                ((\"two\", 0), \"eight\"),\n                ((\"two\", 1), \"nine\"),\n                ((\"two\", 2), \"ten\"),\n                ((\"three\",), {\n                    \"eleven\": 12,\n                    \"thirteen\": 14\n                }),\n                ((\"three\", \"eleven\"), 12),\n                ((\"three\", \"thirteen\"), 14),\n            ],\n        )\n"
  },
  {
    "path": "rollbar/test/test_transform.py",
    "content": "import copy\n\nimport rollbar\nfrom rollbar.test import BaseTest\nfrom rollbar.lib.transforms import Transform\nfrom rollbar.lib.transforms.shortener import ShortenerTransform\nfrom rollbar.lib.transforms.scrub_redact import ScrubRedactTransform\nfrom rollbar.lib.transforms.serializable import SerializableTransform\nfrom rollbar.lib.transforms.scrub import ScrubTransform\nfrom rollbar.lib.transforms.scruburl import ScrubUrlTransform\n\n_test_access_token = 'aaaabbbbccccddddeeeeffff00001111'\n_default_settings = copy.deepcopy(rollbar.SETTINGS)\n\n\nclass TransformTest(BaseTest):\n    def setUp(self):\n        rollbar._initialized = False\n        rollbar.SETTINGS = copy.deepcopy(_default_settings)\n        rollbar.init(_test_access_token, locals={'enabled': True}, handler='blocking', timeout=12345)\n\n    def test_default_transforms(self):\n        transforms = {transform.__class__ for transform in rollbar._transforms}\n\n        self.assertEqual({\n            ShortenerTransform,\n            ScrubRedactTransform,\n            SerializableTransform,\n            ScrubUrlTransform,\n        }, transforms)\n\n    def test_add_custom_transform(self):\n        class CustomTransform(Transform):\n            pass\n\n        rollbar._initialized = False\n        rollbar.SETTINGS = copy.deepcopy(_default_settings)\n        rollbar.init(_test_access_token,\n                     locals={'enabled': True},\n                     handler='blocking',\n                     timeout=12345,\n                     custom_transforms=[CustomTransform()])\n\n        transforms = {transform.__class__ for transform in rollbar._transforms}\n        transforms_ordered = [transform.__class__ for transform in rollbar._transforms]\n\n        self.assertEqual({\n            ShortenerTransform,\n            ScrubRedactTransform,\n            SerializableTransform,\n            ScrubUrlTransform,\n            CustomTransform,\n        }, transforms)\n\n        # CustomTransform should be last because it has the default priority of 100\n        self.assertEqual([\n            ShortenerTransform,\n            ScrubRedactTransform,\n            SerializableTransform,\n            ScrubUrlTransform,\n            CustomTransform,\n        ], transforms_ordered)\n\n    def test_add_custom_transform_first(self):\n        class CustomTransform(Transform):\n            priority = 1\n\n        class CustomTransformTwo(Transform):\n            priority = 35\n\n        rollbar._initialized = False\n        rollbar.SETTINGS = copy.deepcopy(_default_settings)\n        rollbar.init(_test_access_token,\n                     locals={'enabled': True},\n                     handler='blocking',\n                     timeout=12345,\n                     custom_transforms=[CustomTransform(), CustomTransformTwo()])\n\n        transforms = [transform.__class__ for transform in rollbar._transforms]\n\n        # Custom transforms should be first and fifth.\n        self.assertEqual([\n            CustomTransform,  # priority 1\n            ShortenerTransform,  # priority 10\n            ScrubRedactTransform,  # priority 20\n            SerializableTransform,  # priority 30\n            CustomTransformTwo,  # priority 35\n            ScrubUrlTransform,  # priority 50\n        ], transforms)\n"
  },
  {
    "path": "rollbar/test/test_traverse.py",
    "content": "from rollbar.lib.transform import Transform\nfrom rollbar.lib.traverse import traverse\n\nfrom rollbar.test import BaseTest\n\n\nclass NamedTuple(tuple):\n    \"\"\"\n    Modeled after NamedTuple and KeyedTuple from SQLAlchemy 0.7 and 0.8.\n    \"\"\"\n\n    def __new__(cls, vals, labels=None):\n        t = tuple.__new__(cls, vals)\n        if labels:\n            t.__dict__.update(zip(labels, vals))\n            t._labels = labels\n        return t\n\n    def keys(self):\n        return [l for l in self._labels if l is not None]\n\n\nclass KeyMemTransform(Transform):\n    \"\"\"\n    A transform that just stores the keys.\n    \"\"\"\n    keys = []\n\n    def default(self, o, key=None):\n        self.keys.append((key, o))\n        return o\n\n\nclass RollbarTraverseTest(BaseTest):\n    \"\"\"\n    Objects that appear to be a namedtuple, like SQLAlchemy's KeyedTuple,\n    will cause an Exception while identifying them if they don't implement\n    the _make method.\n    \"\"\"\n\n    def setUp(self):\n        self.tuple = NamedTuple((1, 2, 3), labels=[\"one\", \"two\", \"three\"])\n\n    def test_base_case(self):\n        self.assertEqual(traverse(self.tuple), (1, 2, 3))\n\n    def test_bad_object(self):\n        setattr(self.tuple, \"_fields\", \"not quite a named tuple\")\n        self.assertEqual(traverse(self.tuple), (1, 2, 3))\n\n    def test_depth_first(self):\n        obj = {\n            \"one\": [\"four\", \"five\", 6, 7],\n            \"two\": (\"eight\", \"nine\", \"ten\"),\n            \"three\": {\n                \"eleven\": 12,\n                \"thirteen\": 14\n            }\n        }\n        transform = KeyMemTransform()\n        transform.keys = []\n\n        traverse(\n            obj,\n            key=(),\n            string_handler=transform.default,\n            tuple_handler=transform.default,\n            namedtuple_handler=transform.default,\n            list_handler=transform.default,\n            set_handler=transform.default,\n            mapping_handler=transform.default,\n            path_handler=transform.default,\n            default_handler=transform.default,\n            circular_reference_handler=transform.default,\n            allowed_circular_reference_types=transform.default,\n        )\n\n        self.assertEqual(\n            transform.keys,\n            [\n                ((\"one\", 0), \"four\"),\n                ((\"one\", 1), \"five\"),\n                ((\"one\", 2), 6),\n                ((\"one\", 3), 7),\n                ((\"one\",), [\"four\", \"five\", 6, 7]),\n                ((\"two\", 0), \"eight\"),\n                ((\"two\", 1), \"nine\"),\n                ((\"two\", 2), \"ten\"),\n                ((\"two\",), (\"eight\", \"nine\", \"ten\")),\n                ((\"three\", \"eleven\"), 12),\n                ((\"three\", \"thirteen\"), 14),\n                ((\"three\",), {\n                    \"eleven\": 12,\n                    \"thirteen\": 14\n                }),\n                ((), {\n                    \"one\": [\"four\", \"five\", 6, 7],\n                    \"two\": (\"eight\", \"nine\", \"ten\"),\n                    \"three\": {\n                        \"eleven\": 12,\n                        \"thirteen\": 14\n                    }\n                }),\n            ],\n        )\n\n    def test_breadth_first(self):\n        obj = {\n            \"one\": [\"four\", \"five\", 6, 7],\n            \"two\": (\"eight\", \"nine\", \"ten\"),\n            \"three\": {\n                \"eleven\": 12,\n                \"thirteen\": 14\n            }\n        }\n        transform = KeyMemTransform()\n        transform.keys = []\n\n        traverse(\n            obj,\n            key=(),\n            string_handler=transform.default,\n            tuple_handler=transform.default,\n            namedtuple_handler=transform.default,\n            list_handler=transform.default,\n            set_handler=transform.default,\n            mapping_handler=transform.default,\n            path_handler=transform.default,\n            default_handler=transform.default,\n            circular_reference_handler=transform.default,\n            allowed_circular_reference_types=transform.default,\n            depth_first=False,\n        )\n\n        self.assertEqual(\n            transform.keys,\n            [\n                ((), {\n                    \"one\": [\"four\", \"five\", 6, 7],\n                    \"two\": (\"eight\", \"nine\", \"ten\"),\n                    \"three\": {\n                        \"eleven\": 12,\n                        \"thirteen\": 14\n                    }\n                }),\n                ((\"one\",), [\"four\", \"five\", 6, 7]),\n                ((\"one\", 0), \"four\"),\n                ((\"one\", 1), \"five\"),\n                ((\"one\", 2), 6),\n                ((\"one\", 3), 7),\n                ((\"two\",), (\"eight\", \"nine\", \"ten\")),\n                ((\"two\", 0), \"eight\"),\n                ((\"two\", 1), \"nine\"),\n                ((\"two\", 2), \"ten\"),\n                ((\"three\",), {\n                    \"eleven\": 12,\n                    \"thirteen\": 14\n                }),\n                ((\"three\", \"eleven\"), 12),\n                ((\"three\", \"thirteen\"), 14),\n            ],\n        )\n"
  },
  {
    "path": "rollbar/test/twisted_tests/__init__.py",
    "content": ""
  },
  {
    "path": "rollbar/test/twisted_tests/test_twisted.py",
    "content": "\"\"\"\nTests for Twisted instrumentation\n\"\"\"\n\nimport json\nimport sys\n\nfrom unittest import mock\n\nimport rollbar\n\n# access token for https://rollbar.com/rollbar/pyrollbar\nTOKEN = '92c10f5616944b81a2e6f3c6493a0ec2'\n\n# Twisted hasn't been fully ported to Python 3 yet, so don't test there.\nALLOWED_PYTHON_VERSION = not sys.version_info[0] == 3\ntry:\n    from twisted.test import proto_helpers\n    from twisted.trial import unittest\n    from twisted.internet import protocol\n    from twisted.python import log\n\n    TWISTED_INSTALLED = True\nexcept ImportError:\n    TWISTED_INSTALLED = False\n\nif ALLOWED_PYTHON_VERSION and TWISTED_INSTALLED:\n    class SquareProtocol(protocol.Protocol):\n        def dataReceived(self, data):\n            try:\n                number = int(data)\n            except ValueError:\n                log.err()\n                self.transport.write('error')\n            else:\n                self.transport.write(str(number**2))\n\n    class SquareFactory(protocol.Factory):\n        protocol = SquareProtocol\n\n    class TwistedTest(unittest.TestCase):\n        def setUp(self):\n            rollbar.init(TOKEN, 'twisted-test')\n            factory = SquareFactory()\n            self.proto = factory.buildProtocol(('127.0.0.1', 0))\n            self.tr = proto_helpers.StringTransport()\n            self.proto.makeConnection(self.tr)\n\n        @mock.patch('rollbar.send_payload')\n        def test_base_case(self, send_payload):\n            self.proto.dataReceived('8')\n            self.assertEqual(int(self.tr.value()), 64)\n\n            self.assertFalse(send_payload.called)\n\n        @mock.patch('rollbar.send_payload')\n        def test_caught_exception(self, send_payload):\n            self.proto.dataReceived('rollbar')\n            self.assertEqual(self.tr.value(), \"error\")\n            errors = self.flushLoggedErrors(ValueError)\n            self.assertEqual(len(errors), 1)\n\n            self.assertTrue(send_payload.called)\n            payload = send_payload.call_args[0][0]\n            data = payload['data']\n\n            self.assertIn('body', data)\n            self.assertEqual(data['body']['trace']['exception']['class'],\n                             'ValueError')\n            self.assertEqual(data['body']['trace']['exception']['message'],\n                             \"invalid literal for int() with base 10: 'rollbar'\")\n\n        # XXX not able to test uncaught exceptions for some reason\n        # @mock.patch('rollbar.send_payload')\n        # def test_uncaught_exception(self, send_payload):\n        #     self.proto.dataReceived([8, 9])\n        #     self.assertEqual(self.tr.value(), \"error\")\n        #     errors = self.flushLoggedErrors(TypeError)\n        #     self.assertEqual(len(errors), 1)\n        #\n        #     self.assertEqual(send_payload.called, True)\n        #     payload = send_payload.call_args[0][0]\n        #     data = payload['data']\n        #\n        #     self.assertIn('body', data)\n        #     self.assertEqual(data['body']['trace']['exception']['class'],\n        #                      'TypeError')\n        #     self.assertEqual(data['body']['trace']['exception']['message'],\n        #                      \"int() argument must be a string or a number, not 'list'\")\n"
  },
  {
    "path": "rollbar/test/utils.py",
    "content": "from collections.abc import Mapping\n\ndef get_public_attrs(obj: Mapping) -> dict:\n    return {k: obj[k] for k in obj if not k.startswith('_')}\n"
  }
]