From 04e33883257bd110b865aae339e01c6a2d3754cb Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Fri, 30 Jun 2023 12:09:46 -0500 Subject: [PATCH 1/5] Removed legacy Python 2 code for v0.17 and type function calls --- .github/workflows/ci.yml | 34 +------ README.md | 18 +++- rollbar/__init__.py | 49 +++------- rollbar/contrib/django/middleware.py | 5 +- rollbar/contrib/pyramid/__init__.py | 2 +- rollbar/lib/__init__.py | 91 +++---------------- rollbar/lib/_async.py | 3 +- rollbar/lib/transform.py | 5 +- rollbar/lib/transforms/__init__.py | 28 ++---- rollbar/lib/transforms/batched.py | 21 +---- rollbar/lib/transforms/scrub.py | 4 +- rollbar/lib/transforms/scruburl.py | 9 +- rollbar/lib/transforms/serializable.py | 29 ++---- rollbar/lib/transforms/shortener.py | 20 ++-- rollbar/lib/traverse.py | 14 ++- rollbar/lib/type_info.py | 11 +-- rollbar/test/__init__.py | 20 +--- rollbar/test/asgi_tests/test_middleware.py | 5 +- rollbar/test/async_tests/test_async.py | 5 +- rollbar/test/fastapi_tests/test_logger.py | 5 +- rollbar/test/fastapi_tests/test_middleware.py | 5 +- rollbar/test/fastapi_tests/test_routing.py | 5 +- rollbar/test/flask_tests/test_flask.py | 5 +- rollbar/test/starlette_tests/test_logger.py | 5 +- .../test/starlette_tests/test_middleware.py | 5 +- rollbar/test/test_lib.py | 3 +- rollbar/test/test_loghandler.py | 39 ++++---- rollbar/test/test_pyramid.py | 5 +- rollbar/test/test_rollbar.py | 82 +++++------------ rollbar/test/test_scrub_redact_transform.py | 11 +-- rollbar/test/test_scrub_transform.py | 7 +- rollbar/test/test_scruburl_transform.py | 33 +++---- rollbar/test/test_serializable_transform.py | 41 +++------ rollbar/test/test_shortener_transform.py | 9 +- rollbar/test/twisted_tests/test_twisted.py | 5 +- setup.py | 20 +--- 36 files changed, 179 insertions(+), 479 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b046c4d..f841cad6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', 3.11] + python-version: [3.6, 3.7, 3.8, 3.9, '3.10', 3.11] framework: - FLASK_VERSION=0.12.5 Werkzeug\>=0.7,\<1.0 - FLASK_VERSION=1.1.4 @@ -34,8 +34,6 @@ jobs: exclude: # Test frameworks on the python versions they support, according to pypi registry # Flask - - framework: FLASK_VERSION=2.2.3 - python-version: 3.5 - framework: FLASK_VERSION=2.2.3 python-version: 3.6 @@ -48,10 +46,6 @@ jobs: python-version: '3.10' - framework: DJANGO_VERSION=1.11.29 python-version: 3.11 - - framework: DJANGO_VERSION=3.2.18 - python-version: 3.5 - - framework: DJANGO_VERSION=4.0.10 - python-version: 3.5 - framework: DJANGO_VERSION=4.0.10 python-version: 3.6 - framework: DJANGO_VERSION=4.0.10 @@ -66,28 +60,9 @@ jobs: # Twisted - framework: TWISTED_VERSION=20.3.0 python-version: 3.11 - - framework: TWISTED_VERSION=21.7.0 - python-version: 3.5 - - framework: TWISTED_VERSION=22.10.0 - python-version: 3.5 - framework: TWISTED_VERSION=22.10.0 python-version: 3.6 - - # Starlette - - framework: STARLETTE_VERSION=0.12.13 httpx==0.18.1 python-multipart==0.0.5 - python-version: 3.5 - - framework: STARLETTE_VERSION=0.14.2 httpx==0.18.1 python-multipart==0.0.5 - python-version: 3.5 - - # Fastapi - - framework: FASTAPI_VERSION=0.40.0 httpx==0.18.1 python-multipart==0.0.5 - python-version: 3.5 - - framework: FASTAPI_VERSION=0.50.0 httpx==0.18.1 python-multipart==0.0.5 - python-version: 3.5 - - framework: FASTAPI_VERSION=0.63.0 httpx==0.18.1 python-multipart==0.0.5 - python-version: 3.5 - steps: - uses: actions/checkout@v2 with: @@ -98,15 +73,10 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install Python 3.5 dependencies - if: ${{ contains(matrix.python-version, '3.5') }} - # typing-extensions dropped support for Python 3.5 in version 4 - run: pip install six==1.16.0 "typing-extensions<4" requests==2.24.0 blinker==1.5 WebOb==1.8.7 - - name: Install Python 3.6 dependencies if: ${{ contains(matrix.python-version, '3.6') }} # typing-extensions dropped support for Python 3.6 in version 4.2 - run: pip install six==1.16.0 "typing-extensions<4.2" requests==2.27.0 blinker==1.5 + run: pip install "typing-extensions<4.2" requests==2.27.0 blinker==1.5 - name: Set the framework run: echo ${{ matrix.framework }} >> $GITHUB_ENV diff --git a/README.md b/README.md index 4b833026..9757f315 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,29 @@ Python notifier for reporting exceptions, errors, and log messages to [Rollbar]( - **Advanced search:** Filter items by many different properties. Learn more about search. - **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. Learn more about Rollbar notifications. +## Versions Supported -# Setup Instructions +| PyRollbar Version | Python Version Compatibility | Support Level | +|-------------------|-----------------------------------------------|---------------------| +| 0.17.0 | 3.6, 3.7. 3.8, 3.9, 3.10, 3.11 | Full | +| 0.16.3 | 2.7, 3.4, 3.5, 3.6, 3.7. 3.8, 3.9, 3.10, 3.11 | Security Fixes Only | + +#### Support Level Definitions + +**Full** - We will support new features of the library and test against all supported versions. + +**Security Fixes Only** - We will only provide critical security fixes for the library. + +## Setup Instructions 1. [Sign up for a Rollbar account](https://rollbar.com/signup) 2. 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. -# Usage and Reference +## Usage and Reference For complete usage instructions and configuration reference, see our [Python SDK docs](https://docs.rollbar.com/docs/python). -# Release History & Changelog +## Release History & Changelog See our [Releases](https://github.com/rollbar/pyrollbar/releases) page for a list of all releases, including changes. diff --git a/rollbar/__init__.py b/rollbar/__init__.py index b22e0942..f1993da9 100644 --- a/rollbar/__init__.py +++ b/rollbar/__init__.py @@ -16,23 +16,18 @@ import uuid import wsgiref.util import warnings +import queue +from urllib.parse import parse_qs, urljoin import requests -import six -from rollbar.lib import events, filters, dict_merge, parse_qs, text, transport, urljoin, iteritems, defaultJSONEncode +from rollbar.lib import events, filters, dict_merge, transport, defaultJSONEncode __version__ = '0.16.4beta2' __log_name__ = 'rollbar' log = logging.getLogger(__log_name__) -try: - # 2.x - import Queue as queue -except ImportError: - # 3.x - import queue # import request objects from various frameworks, if available try: @@ -691,7 +686,7 @@ def prev_page(self): def _resolve_exception_class(idx, filter): cls, level = filter - if isinstance(cls, six.string_types): + if isinstance(cls, str): # Lazily resolve class name parts = cls.split('.') module = '.'.join(parts[:-1]) @@ -830,7 +825,7 @@ def _trace_data(cls, exc, trace): 'frames': frames, 'exception': { 'class': getattr(cls, '__name__', cls.__class__.__name__), - 'message': text(exc), + 'message': str(exc), } } @@ -960,7 +955,7 @@ def _build_base_data(request, level='error'): 'level': level, 'language': 'python %s' % '.'.join(str(x) for x in sys.version_info[:3]), 'notifier': SETTINGS['notifier'], - 'uuid': text(uuid.uuid4()), + 'uuid': str(uuid.uuid4()), } if SETTINGS.get('code_version'): @@ -1015,9 +1010,9 @@ def hasuser(request): return True else: retval = {} if getattr(user, 'id', None): - retval['id'] = text(user.id) + retval['id'] = str(user.id) elif getattr(user, 'user_id', None): - retval['id'] = text(user.user_id) + retval['id'] = str(user.user_id) # id is required, so only include username/email if we have an id if retval.get('id'): @@ -1034,7 +1029,7 @@ def hasuser(request): return True user_id = user_id_prop() if callable(user_id_prop) else user_id_prop if not user_id: return None - return {'id': text(user_id)} + return {'id': str(user_id)} def _get_func_from_frame(frame): @@ -1049,16 +1044,6 @@ def _get_func_from_frame(frame): return func -def _flatten_nested_lists(l): - ret = [] - for x in l: - if isinstance(x, list): - ret.extend(_flatten_nested_lists(x)) - else: - ret.append(x) - return ret - - def _add_locals_data(trace_data, exc_info): if not SETTINGS['locals']['enabled']: return @@ -1093,15 +1078,7 @@ def _add_locals_data(trace_data, exc_info): # Optionally fill in locals for this frame if arginfo.locals and _check_add_locals(cur_frame, frame_num, num_frames): # Get all of the named args - # - # args can be a nested list of args in the case where there - # are anonymous tuple args provided. - # e.g. in Python 2 you can: - # def func((x, (a, b), z)): - # return x + a + b + z - # - # func((1, (1, 2), 3)) - argspec = _flatten_nested_lists(arginfo.args) + argspec = arginfo.args if arginfo.varargs is not None: varargspec = arginfo.varargs @@ -1131,7 +1108,7 @@ def _add_locals_data(trace_data, exc_info): cur_frame['keywordspec'] = keywordspec if _locals: try: - cur_frame['locals'] = dict((k, _serialize_frame_data(v)) for k, v in iteritems(_locals)) + cur_frame['locals'] = {k: _serialize_frame_data(v) for k, v in _locals.items()} except Exception: log.exception('Error while serializing frame data.') @@ -1419,7 +1396,7 @@ def _build_wsgi_request_data(request): if 'QUERY_STRING' in request: request_data['GET'] = parse_qs(request['QUERY_STRING'], keep_blank_values=True) # Collapse single item arrays - request_data['GET'] = dict((k, v[0] if len(v) == 1 else v) for k, v in request_data['GET'].items()) + request_data['GET'] = {k: (v[0] if len(v) == 1 else v) for k, v in request_data['GET'].items()} request_data['headers'] = _extract_wsgi_headers(request.items()) @@ -1548,7 +1525,7 @@ def _build_payload(data): Returns the full payload as a string. """ - for k, v in iteritems(data): + for k, v in data.items(): data[k] = _transform(v, key=(k,)) payload = { diff --git a/rollbar/contrib/django/middleware.py b/rollbar/contrib/django/middleware.py index f68e5d8d..f5f98e5c 100644 --- a/rollbar/contrib/django/middleware.py +++ b/rollbar/contrib/django/middleware.py @@ -86,7 +86,6 @@ def get_payload_data(self, request, exc): from django.core.exceptions import MiddlewareNotUsed from django.conf import settings from django.http import Http404 -from six import reraise try: from django.urls import resolve @@ -311,7 +310,9 @@ def process_response(self, request, response): try: if hasattr(request, '_rollbar_notifier_original_http404_exc_info'): exc_type, exc_value, exc_traceback = request._rollbar_notifier_original_http404_exc_info - reraise(exc_type, exc_value, exc_traceback) + if exc_value is None: + exc_value = Http404() + raise exc_value.with_traceback(exc_traceback) else: raise Http404() except Exception as exc: diff --git a/rollbar/contrib/pyramid/__init__.py b/rollbar/contrib/pyramid/__init__.py index f02c7a79..e133e15b 100644 --- a/rollbar/contrib/pyramid/__init__.py +++ b/rollbar/contrib/pyramid/__init__.py @@ -138,7 +138,7 @@ def hook(request, data): environment = kw.pop('environment', 'production') if kw.get('scrub_fields'): - kw['scrub_fields'] = set([str.strip(x) for x in kw.get('scrub_fields').split('\n') if x]) + kw['scrub_fields'] = {str.strip(x) for x in kw.get('scrub_fields').split('\n') if x} if kw.get('exception_level_filters'): r = DottedNameResolver() diff --git a/rollbar/lib/__init__.py b/rollbar/lib/__init__.py index 78a32d87..9932b124 100644 --- a/rollbar/lib/__init__.py +++ b/rollbar/lib/__init__.py @@ -4,87 +4,22 @@ import os import sys from array import array -import json -try: - # Python 3 - from collections.abc import Mapping -except ImportError: - # Python 2.7 - from collections import Mapping - -import six -from six.moves import urllib +from collections.abc import Mapping -iteritems = six.iteritems -reprlib = six.moves.reprlib - -binary_type = six.binary_type -integer_types = six.integer_types -number_types = integer_types + (float, ) -string_types = six.string_types +binary_type = bytes +integer_types = int +number_types = (float, int) +string_types = str sequence_types = (Mapping, list, tuple, set, frozenset, array, collections.deque) -urlparse = urllib.parse.urlparse -urlsplit = urllib.parse.urlsplit -urlunparse = urllib.parse.urlunparse -urlunsplit = urllib.parse.urlunsplit -parse_qs = urllib.parse.parse_qs -urlencode = urllib.parse.urlencode -urljoin = urllib.parse.urljoin -quote = urllib.parse.quote - - -_version = sys.version_info - -def python_major_version(): - return _version[0] - - -if python_major_version() < 3: - def text(val): - if isinstance(val, (str, unicode)): - return val - - conversion_options = [unicode, lambda x: unicode(x, encoding='utf8')] - for option in conversion_options: - try: - return option(val) - except UnicodeDecodeError: - pass - - return repr(val) - - _map = map - - def map(*args): - return _map(*args) - - def force_lower(val): +def force_lower(val): + try: + return val.lower() + except: return str(val).lower() -else: - def text(val): - return str(val) - - _map = map - - def map(*args): - return list(_map(*args)) - - def force_lower(val): - try: - return val.lower() - except: - return str(val).lower() - - -def do_for_python_version(two_fn, three_fn, *args, **kw): - if python_major_version() < 3: - return two_fn(*args, **kw) - return three_fn(*args, **kw) - def prefix_match(key, prefixes): if not key: @@ -126,7 +61,7 @@ def key_match(key1, key2): def reverse_list_of_lists(l, apply_each_fn=None): apply_each_fn = apply_each_fn or (lambda x: x) - return map(lambda x: list(reversed(map(apply_each_fn, x))), l or []) + return [reversed([apply_each_fn(x) for x in inner]) for inner in l or []] def build_key_matcher(prefixes_or_suffixes, type='prefix', case_sensitive=False): @@ -183,9 +118,9 @@ def dict_merge(a, b, silence_errors=False): else: try: result[k] = copy.deepcopy(v) - except: + except Exception as e: if not silence_errors: - raise six.reraise(*sys.exc_info()) + raise e result[k] = '' % (v,) @@ -193,7 +128,7 @@ def dict_merge(a, b, silence_errors=False): def circular_reference_label(data, ref_key=None): - ref = '.'.join(map(text, ref_key)) + ref = '.'.join([str(x) for x in ref_key]) return '' % (type(data).__name__, ref) diff --git a/rollbar/lib/_async.py b/rollbar/lib/_async.py index b41dc2f7..1b025258 100644 --- a/rollbar/lib/_async.py +++ b/rollbar/lib/_async.py @@ -4,6 +4,7 @@ import logging import sys from unittest import mock +from urllib.parse import urljoin try: import httpx @@ -12,7 +13,7 @@ import rollbar from rollbar import DEFAULT_TIMEOUT -from rollbar.lib import transport, urljoin +from rollbar.lib import transport log = logging.getLogger(__name__) diff --git a/rollbar/lib/transform.py b/rollbar/lib/transform.py index fa08e5cd..2ba7a030 100644 --- a/rollbar/lib/transform.py +++ b/rollbar/lib/transform.py @@ -23,10 +23,7 @@ def transform_dict(self, o, key=None): def transform_number(self, o, key=None): return self.default(o, key=key) - def transform_py2_str(self, o, key=None): - return self.default(o, key=key) - - def transform_py3_bytes(self, o, key=None): + def transform_bytes(self, o, key=None): return self.default(o, key=key) def transform_unicode(self, o, key=None): diff --git a/rollbar/lib/transforms/__init__.py b/rollbar/lib/transforms/__init__.py index 2869f2d4..d397a986 100644 --- a/rollbar/lib/transforms/__init__.py +++ b/rollbar/lib/transforms/__init__.py @@ -1,12 +1,6 @@ -try: - # Python 3 - from collections.abc import Iterable -except ImportError: - # Python 2.7 - from collections import Iterable +from collections.abc import Iterable from rollbar.lib import ( - python_major_version, binary_type, string_types, integer_types, @@ -56,21 +50,11 @@ def do_transform(type_name, val, key=None, **kw): return val - if python_major_version() < 3: - - def string_handler(s, key=None): - if isinstance(s, str): - return do_transform("py2_str", s, key=key) - elif isinstance(s, unicode): - return do_transform("unicode", s, key=key) - - else: - - def string_handler(s, key=None): - if isinstance(s, bytes): - return do_transform("py3_bytes", s, key=key) - elif isinstance(s, str): - return do_transform("unicode", s, key=key) + def string_handler(s, key=None): + if isinstance(s, bytes): + return do_transform("bytes", s, key=key) + elif isinstance(s, str): + return do_transform("unicode", s, key=key) def default_handler(o, key=None): if isinstance(o, bool): diff --git a/rollbar/lib/transforms/batched.py b/rollbar/lib/transforms/batched.py index 5ba9eadd..b0d5d04d 100644 --- a/rollbar/lib/transforms/batched.py +++ b/rollbar/lib/transforms/batched.py @@ -1,6 +1,5 @@ from rollbar.lib.transform import Transform from rollbar.lib import ( - python_major_version, number_types, type_info, ) @@ -13,21 +12,11 @@ def do_transform(transform, type_name, val, key=None, **kw): return val -if python_major_version() < 3: - - def string_handler(transform, s, key=None): - if isinstance(s, str): - return do_transform(transform, "py2_str", s, key=key) - elif isinstance(s, unicode): - return do_transform(transform, "unicode", s, key=key) - -else: - - def string_handler(transform, s, key=None): - if isinstance(s, bytes): - return do_transform(transform, "py3_bytes", s, key=key) - elif isinstance(s, str): - return do_transform(transform, "unicode", s, key=key) +def string_handler(transform, s, key=None): + if isinstance(s, bytes): + return do_transform(transform, "bytes", s, key=key) + elif isinstance(s, str): + return do_transform(transform, "unicode", s, key=key) def default_handler(transform, o, key=None): diff --git a/rollbar/lib/transforms/scrub.py b/rollbar/lib/transforms/scrub.py index 96158d85..6bbc25ac 100644 --- a/rollbar/lib/transforms/scrub.py +++ b/rollbar/lib/transforms/scrub.py @@ -1,6 +1,6 @@ import random -from rollbar.lib import build_key_matcher, text +from rollbar.lib import build_key_matcher from rollbar.lib.transform import Transform @@ -21,7 +21,7 @@ def redact(self, val): try: _len = len(val) except: - _len = len(text(val)) + _len = len(str(val)) return self.redact_char * _len diff --git a/rollbar/lib/transforms/scruburl.py b/rollbar/lib/transforms/scruburl.py index 7b570462..0ab922cc 100644 --- a/rollbar/lib/transforms/scruburl.py +++ b/rollbar/lib/transforms/scruburl.py @@ -1,6 +1,7 @@ import re +from urllib.parse import urlsplit, urlencode, urlunsplit, parse_qs -from rollbar.lib import iteritems, map, urlsplit, urlencode, urlunsplit, parse_qs, string_types, binary_type +from rollbar.lib import string_types, binary_type from rollbar.lib.transforms.scrub import ScrubTransform @@ -21,7 +22,7 @@ def __init__(self, randomize_len=randomize_len) self.scrub_username = scrub_username self.scrub_password = scrub_password - self.params_to_scrub = set(map(lambda x: x.lower(), params_to_scrub)) + self.params_to_scrub = {x.lower() for x in params_to_scrub or []} def in_scrub_fields(self, key): # Returning True here because we want to scrub URLs out of @@ -51,9 +52,9 @@ def redact(self, url_string): if not netloc: return url_string - for qs_param, vals in iteritems(qs_params): + for qs_param, vals in qs_params.items(): if qs_param.lower() in self.params_to_scrub: - vals2 = map(_redact, vals) + vals2 = [_redact(x) for x in vals] qs_params[qs_param] = vals2 scrubbed_qs = urlencode(qs_params, doseq=True) diff --git a/rollbar/lib/transforms/serializable.py b/rollbar/lib/transforms/serializable.py index 756b9a58..49f95d29 100644 --- a/rollbar/lib/transforms/serializable.py +++ b/rollbar/lib/transforms/serializable.py @@ -4,7 +4,6 @@ from rollbar.lib import ( circular_reference_label, float_infinity_label, float_nan_label, undecodable_object_label, unencodable_object_label) -from rollbar.lib import iteritems, python_major_version, text from rollbar.lib.transform import Transform @@ -25,7 +24,7 @@ def transform_namedtuple(self, o, key=None): for field in tuple_dict: new_vals.append(transformed_dict[field]) - return '<%s>' % text(o._make(new_vals)) + return '<%s>' % str(o._make(new_vals)) def transform_number(self, o, key=None): if math.isnan(o): @@ -35,15 +34,7 @@ def transform_number(self, o, key=None): else: return o - def transform_py2_str(self, o, key=None): - try: - o.decode('utf8') - except UnicodeDecodeError: - return undecodable_object_label(o) - else: - return o - - def transform_py3_bytes(self, o, key=None): + def transform_bytes(self, o, key=None): try: o.decode('utf8') except UnicodeDecodeError: @@ -61,20 +52,14 @@ def transform_unicode(self, o, key=None): def transform_dict(self, o, key=None): ret = {} - for k, v in iteritems(o): + for k, v in o.items(): if isinstance(k, string_types) or isinstance(k, binary_type): - if python_major_version() < 3: - if isinstance(k, unicode): - new_k = self.transform_unicode(k) - else: - new_k = self.transform_py2_str(k) + if isinstance(k, bytes): + new_k = self.transform_bytes(k) else: - if isinstance(k, bytes): - new_k = self.transform_py3_bytes(k) - else: - new_k = self.transform_unicode(k) + new_k = self.transform_unicode(k) else: - new_k = text(k) + new_k = str(k) ret[new_k] = v diff --git a/rollbar/lib/transforms/shortener.py b/rollbar/lib/transforms/shortener.py index 44508e0b..f0392912 100644 --- a/rollbar/lib/transforms/shortener.py +++ b/rollbar/lib/transforms/shortener.py @@ -1,17 +1,13 @@ from array import array import collections import itertools +import reprlib -try: - # Python 3 - from collections.abc import Mapping -except ImportError: - # Python 2.7 - from collections import Mapping +from collections.abc import Mapping from rollbar.lib import ( - integer_types, iteritems, key_in, number_types, reprlib, sequence_types, - string_types, text) + integer_types, key_in, number_types, sequence_types, + string_types) from rollbar.lib.transform import Transform @@ -36,11 +32,11 @@ def __init__(self, safe_repr=True, keys=None, **sizes): self.keys = keys self._repr = reprlib.Repr() - for name, size in iteritems(sizes): + for name, size in sizes.items(): setattr(self._repr, name, size) def _get_max_size(self, obj): - for name, _type in iteritems(_type_name_mapping): + for name, _type in _type_name_mapping.items(): # Special case for dicts since we are using collections.abc.Mapping # to provide better type checking for dict-like objects if name == 'mapping': @@ -66,7 +62,7 @@ def _shorten_mapping(self, obj, max_keys): return {k: obj[k] for k in itertools.islice(obj.keys(), max_keys)} def _shorten_basic(self, obj, max_len): - val = text(obj) + val = str(obj) if len(val) <= max_len: return obj @@ -77,7 +73,7 @@ def _shorten_other(self, obj): return None if self.safe_repr: - obj = text(obj) + obj = str(obj) return self._repr.repr(obj) diff --git a/rollbar/lib/traverse.py b/rollbar/lib/traverse.py index f732771f..6a613dad 100644 --- a/rollbar/lib/traverse.py +++ b/rollbar/lib/traverse.py @@ -1,7 +1,7 @@ import logging -from rollbar.lib import binary_type, iteritems, string_types, circular_reference_label +from rollbar.lib import binary_type, string_types, circular_reference_label # NOTE: Don't remove this line of code as it would cause a breaking change # to the library's API. The items imported here were originally in this file @@ -118,29 +118,27 @@ def traverse( return namedtuple_handler( obj._make( traverse(v, key=key + (k,), **kw) - for k, v in iteritems(obj._asdict()) + for k, v in obj._asdict().items() ), key=key, ) elif obj_type is LIST: return list_handler( - list( - traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(obj) - ), + [traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(obj)], key=key, ) elif obj_type is SET: return set_handler( - set(traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(obj)), + {traverse(elem, key=key + (i,), **kw) for i, elem in enumerate(obj)}, key=key, ) elif obj_type is MAPPING: return mapping_handler( - dict((k, traverse(v, key=key + (k,), **kw)) for k, v in iteritems(obj)), + {k: traverse(v, key=key + (k,), **kw) for k, v in obj.items()}, key=key, ) elif obj_type is DEFAULT: - for handler_type, handler in iteritems(custom_handlers): + for handler_type, handler in custom_handlers.items(): if isinstance(obj, handler_type): return handler(obj, key=key) except: diff --git a/rollbar/lib/type_info.py b/rollbar/lib/type_info.py index a15b44bf..7cf0af54 100644 --- a/rollbar/lib/type_info.py +++ b/rollbar/lib/type_info.py @@ -1,16 +1,7 @@ from rollbar.lib import binary_type, string_types -try: - # Python 3 - from collections.abc import Mapping - from collections.abc import Sequence - from collections.abc import Set -except ImportError: - # Python 2.7 - from collections import Mapping - from collections import Sequence - from collections import Set +from collections.abc import Mapping, Sequence, Set CIRCULAR = -1 diff --git a/rollbar/test/__init__.py b/rollbar/test/__init__.py index 9a6903ad..5f52f1bf 100644 --- a/rollbar/test/__init__.py +++ b/rollbar/test/__init__.py @@ -1,5 +1,4 @@ import unittest -import sys SNOWMAN = b'\xe2\x98\x83' @@ -10,24 +9,7 @@ class BaseTest(unittest.TestCase): pass -class SkipAsyncTestLoader(unittest.TestLoader): - """ - Python 2 does not have the async keyword, so when tests are run under python 2.7 the loader - will fail with a syntaxerror. This loader class does the following: - - try to load as normal - - if loading fails because of a syntax error in python < 3.4, skip the file. - """ - def _get_module_from_name(self, name): - try: - return super(SkipAsyncTestLoader, self)._get_module_from_name(name) - except SyntaxError as e: - if sys.version_info < (3, 5): - return None - else: - raise - - def discover(): - loader = SkipAsyncTestLoader() + loader = unittest.TestLoader() suite = loader.discover(__name__) return suite diff --git a/rollbar/test/asgi_tests/test_middleware.py b/rollbar/test/asgi_tests/test_middleware.py index 28ac42a0..d949d596 100644 --- a/rollbar/test/asgi_tests/test_middleware.py +++ b/rollbar/test/asgi_tests/test_middleware.py @@ -2,10 +2,7 @@ import importlib import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock import unittest diff --git a/rollbar/test/async_tests/test_async.py b/rollbar/test/async_tests/test_async.py index 9779f587..6843c8ec 100644 --- a/rollbar/test/async_tests/test_async.py +++ b/rollbar/test/async_tests/test_async.py @@ -1,10 +1,7 @@ import copy import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock import unittest diff --git a/rollbar/test/fastapi_tests/test_logger.py b/rollbar/test/fastapi_tests/test_logger.py index 4ef2cdb0..49a4a8a6 100644 --- a/rollbar/test/fastapi_tests/test_logger.py +++ b/rollbar/test/fastapi_tests/test_logger.py @@ -1,10 +1,7 @@ import importlib import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock try: import fastapi diff --git a/rollbar/test/fastapi_tests/test_middleware.py b/rollbar/test/fastapi_tests/test_middleware.py index 6cecf48b..c49336e4 100644 --- a/rollbar/test/fastapi_tests/test_middleware.py +++ b/rollbar/test/fastapi_tests/test_middleware.py @@ -2,10 +2,7 @@ import importlib import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock try: import fastapi diff --git a/rollbar/test/fastapi_tests/test_routing.py b/rollbar/test/fastapi_tests/test_routing.py index bd7f6e71..c26ca159 100644 --- a/rollbar/test/fastapi_tests/test_routing.py +++ b/rollbar/test/fastapi_tests/test_routing.py @@ -3,10 +3,7 @@ import json import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock try: import fastapi diff --git a/rollbar/test/flask_tests/test_flask.py b/rollbar/test/flask_tests/test_flask.py index f8b020e3..b97227bb 100644 --- a/rollbar/test/flask_tests/test_flask.py +++ b/rollbar/test/flask_tests/test_flask.py @@ -6,10 +6,7 @@ import sys import os -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock import rollbar diff --git a/rollbar/test/starlette_tests/test_logger.py b/rollbar/test/starlette_tests/test_logger.py index 6c7b61d7..3ed51e68 100644 --- a/rollbar/test/starlette_tests/test_logger.py +++ b/rollbar/test/starlette_tests/test_logger.py @@ -1,10 +1,7 @@ import importlib import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock try: import starlette diff --git a/rollbar/test/starlette_tests/test_middleware.py b/rollbar/test/starlette_tests/test_middleware.py index fcc26136..7c9f6554 100644 --- a/rollbar/test/starlette_tests/test_middleware.py +++ b/rollbar/test/starlette_tests/test_middleware.py @@ -2,10 +2,7 @@ import importlib import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock try: import starlette diff --git a/rollbar/test/test_lib.py b/rollbar/test/test_lib.py index 1facb933..3156148b 100644 --- a/rollbar/test/test_lib.py +++ b/rollbar/test/test_lib.py @@ -2,7 +2,6 @@ from rollbar.test import BaseTest -import six class RollbarLibTest(BaseTest): def test_dict_merge_not_dict(self): @@ -58,4 +57,4 @@ def test_dict_merge_dicts_select_poll(self): self.assertIn('b', result['a']) self.assertEqual(42, result['a']['b']) self.assertIn('y', result['a']) - six.assertRegex(self, result['a']['y'], r'Uncopyable obj') + self.assertRegex(result['a']['y'], r'Uncopyable obj') diff --git a/rollbar/test/test_loghandler.py b/rollbar/test/test_loghandler.py index 9ec8a1b9..a5defb3c 100644 --- a/rollbar/test/test_loghandler.py +++ b/rollbar/test/test_loghandler.py @@ -5,10 +5,7 @@ import logging import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock import rollbar from rollbar.logger import RollbarHandler @@ -93,25 +90,23 @@ def test_request_is_get_from_log_record_if_present(self): logger.warning("Warning message", extra={"request": request}) self.assertEqual(report_message_mock.call_args[1]["request"], request) - # Python 2.6 doesnt support extra param in logger.exception. - if not sys.version_info[:2] == (2, 6): - # if you call logger.exception outside of an exception - # handler, it shouldn't try to report exc_info, since it - # won't have any - with mock.patch("rollbar.report_exc_info") as report_exc_info: - with mock.patch("rollbar.report_message") as report_message_mock: + # if you call logger.exception outside of an exception + # handler, it shouldn't try to report exc_info, since it + # won't have any + with mock.patch("rollbar.report_exc_info") as report_exc_info: + with mock.patch("rollbar.report_message") as report_message_mock: + logger.exception("Exception message", extra={"request": request}) + report_exc_info.assert_not_called() + self.assertEqual(report_message_mock.call_args[1]["request"], request) + + with mock.patch("rollbar.report_exc_info") as report_exc_info: + with mock.patch("rollbar.report_message") as report_message_mock: + try: + raise Exception() + except: logger.exception("Exception message", extra={"request": request}) - report_exc_info.assert_not_called() - self.assertEqual(report_message_mock.call_args[1]["request"], request) - - with mock.patch("rollbar.report_exc_info") as report_exc_info: - with mock.patch("rollbar.report_message") as report_message_mock: - try: - raise Exception() - except: - logger.exception("Exception message", extra={"request": request}) - self.assertEqual(report_exc_info.call_args[1]["request"], request) - report_message_mock.assert_not_called() + self.assertEqual(report_exc_info.call_args[1]["request"], request) + report_message_mock.assert_not_called() @mock.patch('rollbar.send_payload') def test_nested_exception_trace_chain(self, send_payload): diff --git a/rollbar/test/test_pyramid.py b/rollbar/test/test_pyramid.py index 4f643bbd..63c4a3ca 100644 --- a/rollbar/test/test_pyramid.py +++ b/rollbar/test/test_pyramid.py @@ -1,7 +1,4 @@ -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock from rollbar.test import BaseTest diff --git a/rollbar/test/test_rollbar.py b/rollbar/test/test_rollbar.py index 260aa8b5..92f9874d 100644 --- a/rollbar/test/test_rollbar.py +++ b/rollbar/test/test_rollbar.py @@ -11,16 +11,13 @@ from StringIO import StringIO except ImportError: from io import StringIO -try: - from unittest import mock -except ImportError: - import mock + +from unittest import mock import unittest -import six import rollbar -from rollbar.lib import python_major_version, string_types +from rollbar.lib import string_types from rollbar.test import BaseTest @@ -464,18 +461,8 @@ def test_get_request_fastapi_middleware(self): app = FastAPI() app.add_middleware(ReporterMiddleware) - # Inject annotations and decorate endpoint dynamically - # to avoid SyntaxError for older Python - # - # This is the code we'd use if we had not loaded the test file on Python 2. - # - # @app.get('/{param}') - # def root(param, fastapi_request: Request): - # current_request = rollbar.get_request() - # - # self.assertEqual(current_request, fastapi_request) - - def root(param, fastapi_request): + @app.get('/{param}') + def root(param, fastapi_request: Request): current_request = rollbar.get_request() self.assertEqual(current_request, fastapi_request) @@ -501,18 +488,8 @@ def test_get_request_fastapi_logger(self): app = FastAPI() app.add_middleware(ReporterMiddleware) - # Inject annotations and decorate endpoint dynamically - # to avoid SyntaxError for older Python - # - # This is the code we'd use if we had not loaded the test file on Python 2. - # - # @app.get('/{param}') - # def root(fastapi_request: Request): - # current_request = rollbar.get_request() - # - # self.assertEqual(current_request, fastapi_request) - - def root(param, fastapi_request): + @app.get('/{param}') + def root(fastapi_request: Request): current_request = rollbar.get_request() self.assertEqual(current_request, fastapi_request) @@ -542,18 +519,8 @@ def test_get_request_fastapi_router(self): app = FastAPI() rollbar_add_to(app) - # Inject annotations and decorate endpoint dynamically - # to avoid SyntaxError for older Python - # - # This is the code we'd use if we had not loaded the test file on Python 2. - # - # @app.get('/{param}') - # def root(fastapi_request: Request): - # current_request = rollbar.get_request() - # - # self.assertEqual(current_request, fastapi_request) - - def root(param, fastapi_request): + @app.get('/{param}') + def root(fastapi_request: Request): current_request = rollbar.get_request() self.assertEqual(current_request, fastapi_request) @@ -1202,7 +1169,7 @@ def test_args_lambda_with_star_args(self, send_payload): varargs = payload['data']['body']['trace']['frames'][-1]['varargspec'] self.assertEqual(1, len(payload['data']['body']['trace']['frames'][-1]['locals'][varargs])) - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals'][varargs][0], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][varargs][0], r'\*+') @mock.patch('rollbar.send_payload') def test_args_lambda_with_star_args_and_args(self, send_payload): @@ -1229,8 +1196,8 @@ def test_args_lambda_with_star_args_and_args(self, send_payload): self.assertEqual('arg1-value', payload['data']['body']['trace']['frames'][-1]['locals']['arg1']) self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['locals'][varargs])) - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals'][varargs][0], r'\*+') - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals'][varargs][1], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][varargs][0], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][varargs][1], r'\*+') @mock.patch('rollbar.send_payload') def test_args_lambda_with_kwargs(self, send_payload): @@ -1395,8 +1362,8 @@ def _raise(password='sensitive', clear='text'): self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['argspec'])) self.assertEqual('password', payload['data']['body']['trace']['frames'][-1]['argspec'][0]) - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals']['password'], r'\*+') - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals']['headers']['Authorization'], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['password'], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['headers']['Authorization'], r'\*+') self.assertEqual('clear', payload['data']['body']['trace']['frames'][-1]['argspec'][1]) self.assertEqual('text', payload['data']['body']['trace']['frames'][-1]['locals']['clear']) @@ -1450,7 +1417,7 @@ def _raise(**kwargs): self.assertEqual(2, len(payload['data']['body']['trace']['frames'][-1]['locals'][keywords])) self.assertIn('password', payload['data']['body']['trace']['frames'][-1]['locals'][keywords]) - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['password'], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['password'], r'\*+') self.assertIn('clear', payload['data']['body']['trace']['frames'][-1]['locals'][keywords]) self.assertEqual('text', payload['data']['body']['trace']['frames'][-1]['locals'][keywords]['clear']) @@ -1481,12 +1448,11 @@ def _raise(): payload = send_payload.call_args[0][0] - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals']['password'], r'\*+') - six.assertRegex(self, payload['data']['body']['trace']['frames'][-1]['locals']['Password'], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['password'], r'\*+') + self.assertRegex(payload['data']['body']['trace']['frames'][-1]['locals']['Password'], r'\*+') self.assertIn('_invalid', payload['data']['body']['trace']['frames'][-1]['locals']) - binary_type_name = 'str' if python_major_version() < 3 else 'bytes' - undecodable_message = '' % (binary_type_name, base64.b64encode(invalid).decode('ascii')) + undecodable_message = '' % ('bytes', base64.b64encode(invalid).decode('ascii')) self.assertEqual(undecodable_message, payload['data']['body']['trace']['frames'][-1]['locals']['_invalid']) @mock.patch('rollbar.send_payload') @@ -1799,19 +1765,19 @@ def test_scrub_webob_request_data(self): self.assertEqual('I am from NSA', unscrubbed['headers']['Authorization']) scrubbed = rollbar._transform(unscrubbed) - six.assertRegex(self, scrubbed['url'], r'http://example.com/the/path\?(q=hello&password=-+)|(password=-+&q=hello)') + self.assertRegex(scrubbed['url'], r'http://example.com/the/path\?(q=hello&password=-+)|(password=-+&q=hello)') self.assertEqual(scrubbed['GET']['q'], 'hello') - six.assertRegex(self, scrubbed['GET']['password'], r'\*+') + self.assertRegex(scrubbed['GET']['password'], r'\*+') self.assertEqual(scrubbed['POST']['foo'], 'bar') - six.assertRegex(self, scrubbed['POST']['confirm_password'], r'\*+') - six.assertRegex(self, scrubbed['POST']['token'], r'\*+') + self.assertRegex(scrubbed['POST']['confirm_password'], r'\*+') + self.assertRegex(scrubbed['POST']['token'], r'\*+') self.assertEqual('5.6.7.8', scrubbed['headers']['X-Real-Ip']) - six.assertRegex(self, scrubbed['headers']['Cookies'], r'\*+') - six.assertRegex(self, scrubbed['headers']['Authorization'], r'\*+') + self.assertRegex(scrubbed['headers']['Cookies'], r'\*+') + self.assertRegex(scrubbed['headers']['Authorization'], r'\*+') def test_filter_ip_no_user_ip(self): request_data = {'something': 'but no ip'} diff --git a/rollbar/test/test_scrub_redact_transform.py b/rollbar/test/test_scrub_redact_transform.py index 34e3356d..da6abe4b 100644 --- a/rollbar/test/test_scrub_redact_transform.py +++ b/rollbar/test/test_scrub_redact_transform.py @@ -1,11 +1,6 @@ -try: - # Python 3 - from collections.abc import Mapping -except ImportError: - # Python 2.7 - from collections import Mapping +from collections.abc import Mapping -from rollbar.lib import text, transforms +from rollbar.lib import transforms from rollbar.lib.transforms.scrub_redact import ScrubRedactTransform, REDACT_REF from rollbar.test import BaseTest @@ -19,7 +14,7 @@ class NotRedactRef(): try: SCRUBBED = '*' * len(REDACT_REF) except: - SCRUBBED = '*' * len(text(REDACT_REF)) + SCRUBBED = '*' * len(str(REDACT_REF)) class ScrubRedactTransformTest(BaseTest): diff --git a/rollbar/test/test_scrub_transform.py b/rollbar/test/test_scrub_transform.py index 7cc50eb1..be74b61b 100644 --- a/rollbar/test/test_scrub_transform.py +++ b/rollbar/test/test_scrub_transform.py @@ -1,11 +1,6 @@ import copy -try: - # Python 3 - from collections.abc import Mapping -except ImportError: - # Python 2.7 - from collections import Mapping +from collections.abc import Mapping from rollbar.lib import transforms from rollbar.lib.transforms.scrub import ScrubTransform diff --git a/rollbar/test/test_scruburl_transform.py b/rollbar/test/test_scruburl_transform.py index b0a63e08..03a0013c 100644 --- a/rollbar/test/test_scruburl_transform.py +++ b/rollbar/test/test_scruburl_transform.py @@ -1,15 +1,12 @@ -import copy -import six +from urllib.parse import urlparse, parse_qs -from rollbar.lib import map, transforms, string_types, urlparse, parse_qs, python_major_version +from rollbar.lib import transforms, string_types from rollbar.lib.transforms.scruburl import ScrubUrlTransform, _starts_with_auth_re -from rollbar.test import BaseTest, SNOWMAN, SNOWMAN_UNICODE +from rollbar.test import BaseTest, SNOWMAN_UNICODE -if python_major_version() >= 3: - SNOWMAN = SNOWMAN_UNICODE -SNOWMAN_LEN = len(SNOWMAN) +SNOWMAN_LEN = len(SNOWMAN_UNICODE) class ScrubUrlTransformTest(BaseTest): @@ -43,17 +40,17 @@ def _compare_urls(self, url1, url2): if _starts_with_auth_re.match(url2): url2 = '//%s' % url2 - parsed_urls = map(urlparse, (url1, url2)) - qs_params = map(lambda x: parse_qs(x.query, keep_blank_values=True), parsed_urls) - num_params = map(len, qs_params) - param_names = map(lambda x: set(x.keys()), qs_params) + parsed_urls = [urlparse(url) for url in (url1, url2)] + qs_params = [parse_qs(x.query, keep_blank_values=True) for x in parsed_urls] + num_params = [len(x) for x in qs_params] + param_names = [set(x.keys()) for x in qs_params] self.assertEqual(*num_params) self.assertDictEqual(*qs_params) self.assertSetEqual(*param_names) for facet in ('scheme', 'netloc', 'path', 'params', 'username', 'password', 'hostname', 'port'): - comp = map(lambda x: getattr(x, facet), parsed_urls) + comp = [getattr(x, facet) for x in parsed_urls] self.assertEqual(*comp) def test_no_scrub(self): @@ -72,14 +69,14 @@ def test_scrub_simple_url_params(self): self._assertScrubbed(['password'], obj, expected) def test_scrub_utf8_url_params(self): - obj = 'http://foo.com/asdf?password=%s' % SNOWMAN - expected = obj.replace(SNOWMAN, '-' * SNOWMAN_LEN) + obj = 'http://foo.com/asdf?password=%s' % SNOWMAN_UNICODE + expected = obj.replace(SNOWMAN_UNICODE, '-' * SNOWMAN_LEN) self._assertScrubbed(['password'], obj, expected) def test_scrub_utf8_url_keys(self): - obj = 'http://foo.com/asdf?%s=secret' % SNOWMAN + obj = 'http://foo.com/asdf?%s=secret' % SNOWMAN_UNICODE expected = obj.replace('secret', '------') - self._assertScrubbed([str(SNOWMAN)], obj, expected) + self._assertScrubbed([str(SNOWMAN_UNICODE)], obj, expected) def test_scrub_multi_url_params(self): obj = 'http://foo.com/asdf?password=secret&password=secret2&token=TOK&clear=text' @@ -147,5 +144,5 @@ def test_scrub_dict_nested_key_match_with_circular_ref(self): self.assertNotIn('secret', result['url'][0]['link']) self.assertNotIn('secr3t', result['link'][0]['url']) self.assertNotIn('secret', result['link'][0]['url']) - six.assertNotRegex(self, result['url'][0]['link'], r'^-+$') - six.assertNotRegex(self, result['link'][0]['url'], r'^-+$') + self.assertNotRegex(result['url'][0]['link'], r'^-+$') + self.assertNotRegex(result['link'][0]['url'], r'^-+$') diff --git a/rollbar/test/test_serializable_transform.py b/rollbar/test/test_serializable_transform.py index 3a3cab85..6978d7c0 100644 --- a/rollbar/test/test_serializable_transform.py +++ b/rollbar/test/test_serializable_transform.py @@ -4,24 +4,14 @@ import enum import sys -try: - # Python 3 - from collections.abc import Mapping -except ImportError: - # Python 2.7 - from collections import Mapping +from collections.abc import Mapping -import six - -from rollbar.lib import transforms, python_major_version +from rollbar.lib import transforms from rollbar.lib.transforms.serializable import SerializableTransform -from rollbar.test import BaseTest, SNOWMAN, SNOWMAN_UNICODE - -if python_major_version() >= 3: - SNOWMAN = SNOWMAN_UNICODE +from rollbar.test import BaseTest, SNOWMAN_UNICODE -SNOWMAN_LEN = len(SNOWMAN) +SNOWMAN_LEN = len(SNOWMAN_UNICODE) # This base64 encoded string contains bytes that do not @@ -29,8 +19,7 @@ invalid_b64 = b'CuX2JKuXuLVtJ6l1s7DeeQ==' invalid = base64.b64decode(invalid_b64) -binary_type_name = 'str' if python_major_version() < 3 else 'bytes' -undecodable_repr = '' % (binary_type_name, invalid_b64.decode('ascii')) +undecodable_repr = f'' class SerializableTransformTest(BaseTest): @@ -171,10 +160,7 @@ def test_encode_namedtuple(self): nt = MyType(field_1='this is field 1', field_2=invalid) start = nt - if python_major_version() < 3: - expected = "" % undecodable_repr - else: - expected = "" % undecodable_repr + expected = "" % undecodable_repr self._assertSerialized(start, expected) @@ -243,10 +229,7 @@ def __repr__(self): serializable = SerializableTransform(safelist_types=[CustomRepr]) result = transforms.transform(start, serializable) - if python_major_version() < 3: - self.assertEqual(result['custom'], b'hello') - else: - six.assertRegex(self, result['custom'], "") + self.assertRegex(result['custom'], "") def test_encode_with_custom_repr_returns_object(self): class CustomRepr(object): @@ -257,16 +240,16 @@ def __repr__(self): serializable = SerializableTransform(safelist_types=[CustomRepr]) result = transforms.transform(start, serializable) - six.assertRegex(self, result['custom'], "") + self.assertRegex(result['custom'], "") def test_encode_with_custom_repr_returns_unicode(self): class CustomRepr(object): def __repr__(self): - return SNOWMAN + return SNOWMAN_UNICODE start = {'hello': 'world', 'custom': CustomRepr()} expected = copy.deepcopy(start) - expected['custom'] = SNOWMAN + expected['custom'] = SNOWMAN_UNICODE self._assertSerialized(start, expected, safelist=[CustomRepr]) def test_encode_with_bad_repr_doesnt_die(self): @@ -277,7 +260,7 @@ def __repr__(self): start = {'hello': 'world', 'custom': CustomRepr()} serializable = SerializableTransform(safelist_types=[CustomRepr]) result = transforms.transform(start, serializable) - six.assertRegex(self, result['custom'], "") + self.assertRegex(result['custom'], "") def test_encode_with_bad_str_doesnt_die(self): @@ -293,4 +276,4 @@ def __repr__(self): start = {'hello': 'world', 'custom': CustomRepr()} serializable = SerializableTransform(safelist_types=[CustomRepr]) result = transforms.transform(start, serializable) - six.assertRegex(self, result['custom'], "") + self.assertRegex(result['custom'], "") diff --git a/rollbar/test/test_shortener_transform.py b/rollbar/test/test_shortener_transform.py index acb91a5b..55180c34 100644 --- a/rollbar/test/test_shortener_transform.py +++ b/rollbar/test/test_shortener_transform.py @@ -2,7 +2,6 @@ from array import array from collections import deque -import six from rollbar import DEFAULT_LOCALS_SIZES from rollbar.lib import transforms from rollbar.lib.transforms.shortener import ShortenerTransform @@ -71,9 +70,7 @@ def test_shorten_string(self): self._assert_shortened('string', expected) def test_shorten_long(self): - expected = '179556827339164684...002504519623752387L' - if six.PY3: - expected = '179556827339164684...5002504519623752387' + expected = '179556827339164684...5002504519623752387' self._assert_shortened('long', expected) def test_shorten_mapping(self): @@ -114,9 +111,7 @@ def test_shorten_deque(self): self._assert_shortened('deque', expected) def test_shorten_other(self): - expected = '= "3.6"', + 'httpx', 'aiocontextvars; python_version == "3.6"' ] @@ -46,17 +44,14 @@ url='http://github.com/rollbar/pyrollbar', classifiers=[ "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3 :: Only", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Development Status :: 5 - Production/Stable", @@ -78,16 +73,7 @@ "Topic :: System :: Monitoring", ], install_requires=[ - # The currently used version of `setuptools` has a bug, - # so the version requirements are not properly respected. - # - # In the current version, `requests>= 0.12.1` - # always installs the latest version of the package. - 'requests>=0.12.1; python_version == "2.7"', - 'requests>=0.12.1; python_version >= "3.6"', - 'requests>=0.12.1; python_version == "3.5"', - 'requests>=0.12.1; python_version == "3.4"', - 'six>=1.9.0' + 'requests>=0.12.1', ], tests_require=tests_require, ) From 53c472e13c8ac1b963b4dfe90ac9d35a7a223f3c Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Thu, 3 Aug 2023 04:45:16 -0500 Subject: [PATCH 2/5] Cleaned up the key and prefix matching logic --- rollbar/lib/__init__.py | 25 ++++++++++++------------- rollbar/lib/transforms/__init__.py | 2 -- rollbar/lib/transforms/scrub.py | 6 +++++- rollbar/test/test_lib.py | 10 +++++++++- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/rollbar/lib/__init__.py b/rollbar/lib/__init__.py index 9932b124..8eb7be69 100644 --- a/rollbar/lib/__init__.py +++ b/rollbar/lib/__init__.py @@ -1,8 +1,6 @@ import base64 import collections import copy -import os -import sys from array import array from collections.abc import Mapping @@ -26,8 +24,10 @@ def prefix_match(key, prefixes): return False for prefix in prefixes: - common_prefix = os.path.commonprefix((prefix, key)) - if common_prefix == prefix: + if len(prefix) > len(key): + continue + + if prefix == key[:len(prefix)]: return True return False @@ -45,18 +45,17 @@ def key_in(key, keys): def key_match(key1, key2): - key1_len = len(key1) - key2_len = len(key2) - if key1_len != key2_len: + if len(key1) != len(key2): return False - z_key = zip(key1, key2) - num_matches = 0 - for p1, p2 in z_key: - if '*' in (p1, p2) or p1 == p2: - num_matches += 1 + for p1, p2 in zip(key1, key2): + if '*' == p1 or '*' == p2: + continue + if p1 == p2: + continue + return False - return num_matches == key1_len + return True def reverse_list_of_lists(l, apply_each_fn=None): diff --git a/rollbar/lib/transforms/__init__.py b/rollbar/lib/transforms/__init__.py index d397a986..3f1b8802 100644 --- a/rollbar/lib/transforms/__init__.py +++ b/rollbar/lib/transforms/__init__.py @@ -3,10 +3,8 @@ from rollbar.lib import ( binary_type, string_types, - integer_types, number_types, traverse, - type_info, ) # NOTE: Don't remove this import, it would cause a breaking change to the library's API. # The `Transform` class was moved out of this file to prevent a cyclical dependency issue. diff --git a/rollbar/lib/transforms/scrub.py b/rollbar/lib/transforms/scrub.py index 6bbc25ac..ff0af810 100644 --- a/rollbar/lib/transforms/scrub.py +++ b/rollbar/lib/transforms/scrub.py @@ -5,13 +5,17 @@ class ScrubTransform(Transform): + suffix_matcher = None def __init__(self, suffixes=None, redact_char='*', randomize_len=True): super(ScrubTransform, self).__init__() - self.suffix_matcher = build_key_matcher(suffixes, type='suffix') + if suffixes is not None and len(suffixes) > 0: + self.suffix_matcher = build_key_matcher(suffixes, type='suffix') self.redact_char = redact_char self.randomize_len = randomize_len def in_scrub_fields(self, key): + if self.suffix_matcher is None: + return False return self.suffix_matcher(key) def redact(self, val): diff --git a/rollbar/test/test_lib.py b/rollbar/test/test_lib.py index 3156148b..7d792638 100644 --- a/rollbar/test/test_lib.py +++ b/rollbar/test/test_lib.py @@ -1,4 +1,4 @@ -from rollbar.lib import dict_merge +from rollbar.lib import dict_merge, prefix_match from rollbar.test import BaseTest @@ -11,6 +11,14 @@ def test_dict_merge_not_dict(self): self.assertEqual(99, result) + def test_prefix_match(self): + key = ['password', 'argspec', '0'] + self.assertTrue(prefix_match(key, [['password']])) + + def test_prefix_match(self): + key = ['environ', 'argspec', '0'] + self.assertFalse(prefix_match(key, [['password']])) + def test_dict_merge_dicts_independent(self): a = {'a': {'b': 42}} b = {'x': {'y': 99}} From dcb6adda6d0791b63bb37eb9814222b899584d3e Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Thu, 3 Aug 2023 04:46:51 -0500 Subject: [PATCH 3/5] Bumped version to 1.0.0 beta 0. --- rollbar/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollbar/__init__.py b/rollbar/__init__.py index f1993da9..247f5fc6 100644 --- a/rollbar/__init__.py +++ b/rollbar/__init__.py @@ -24,7 +24,7 @@ from rollbar.lib import events, filters, dict_merge, transport, defaultJSONEncode -__version__ = '0.16.4beta2' +__version__ = '1.0.0b0' __log_name__ = 'rollbar' log = logging.getLogger(__log_name__) From e054667e74cd33344059eb4bbb3573367c0d04b3 Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Fri, 18 Aug 2023 16:10:04 -0500 Subject: [PATCH 4/5] Fixed immutables 0.20 incompatible with Python 3.6 and 3.7 --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f841cad6..59a77d18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,6 @@ jobs: matrix: python-version: [3.6, 3.7, 3.8, 3.9, '3.10', 3.11] framework: - - FLASK_VERSION=0.12.5 Werkzeug\>=0.7,\<1.0 - FLASK_VERSION=1.1.4 - FLASK_VERSION=2.2.3 - DJANGO_VERSION=1.11.29 @@ -76,7 +75,12 @@ jobs: - name: Install Python 3.6 dependencies if: ${{ contains(matrix.python-version, '3.6') }} # typing-extensions dropped support for Python 3.6 in version 4.2 - run: pip install "typing-extensions<4.2" requests==2.27.0 blinker==1.5 + run: pip install "typing-extensions<4.2" requests==2.27.0 blinker==1.5 immutables==0.19 + + - name: Install Python 3.7 dependencies + if: ${{ contains(matrix.python-version, '3.7') }} + # immutables dropped support for Python<3.8 in version 0.20 + run: pip install immutables==0.19 - name: Set the framework run: echo ${{ matrix.framework }} >> $GITHUB_ENV From bfaab771c68b9a24d2e2a71cce008bde03091c15 Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Fri, 18 Aug 2023 16:27:36 -0500 Subject: [PATCH 5/5] Added KeyError to appengine import error handling --- rollbar/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollbar/__init__.py b/rollbar/__init__.py index 247f5fc6..1e3b7cb7 100644 --- a/rollbar/__init__.py +++ b/rollbar/__init__.py @@ -81,7 +81,7 @@ try: from google.appengine.api.urlfetch import fetch as AppEngineFetch -except ImportError: +except (ImportError, KeyError): AppEngineFetch = None try: