diff --git a/redash/cli/data_sources.py b/redash/cli/data_sources.py index 8049398659..4a94bfd12d 100644 --- a/redash/cli/data_sources.py +++ b/redash/cli/data_sources.py @@ -2,7 +2,6 @@ import click from flask.cli import AppGroup -from six import text_type from sqlalchemy.orm.exc import NoResultFound from redash import models @@ -124,7 +123,7 @@ def new(name=None, type=None, options=None, organization="default"): schema = query_runner.configuration_schema() if options is None: - types = {"string": text_type, "number": int, "boolean": bool} + types = {"string": str, "number": int, "boolean": bool} options_obj = {} diff --git a/redash/cli/users.py b/redash/cli/users.py index af7ba99284..dacc05096d 100644 --- a/redash/cli/users.py +++ b/redash/cli/users.py @@ -2,7 +2,6 @@ from click import BOOL, argument, option, prompt from flask.cli import AppGroup -from six import string_types from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.exc import IntegrityError @@ -13,7 +12,7 @@ def build_groups(org, groups, is_admin): - if isinstance(groups, string_types): + if isinstance(groups, str): groups = groups.split(",") groups.remove("") # in case it was empty string groups = [int(g) for g in groups] diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py index 4dee409696..05a3f2856b 100644 --- a/redash/handlers/data_sources.py +++ b/redash/handlers/data_sources.py @@ -3,7 +3,6 @@ from flask import make_response, request from flask_restful import abort from funcy import project -from six import text_type from sqlalchemy.exc import IntegrityError from redash import models @@ -254,7 +253,7 @@ def post(self, data_source_id): try: data_source.query_runner.test_connection() except Exception as e: - response = {"message": text_type(e), "ok": False} + response = {"message": str(e), "ok": False} else: response = {"message": "success", "ok": True} diff --git a/redash/models/__init__.py b/redash/models/__init__.py index 3022b98e0a..dd69fe8f6f 100644 --- a/redash/models/__init__.py +++ b/redash/models/__init__.py @@ -5,7 +5,6 @@ import numbers import pytz -from six import text_type from sqlalchemy import distinct, or_, and_, UniqueConstraint from sqlalchemy.dialects import postgresql from sqlalchemy.event import listens_for @@ -143,7 +142,7 @@ def to_dict(self, all=False, with_permissions_for=None): return d def __str__(self): - return text_type(self.name) + return str(self.name) @classmethod def create_with_group(cls, *args, **kwargs): @@ -486,7 +485,7 @@ class Query(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model): __mapper_args__ = {"version_id_col": version, "version_id_generator": False} def __str__(self): - return text_type(self.id) + return str(self.id) def archive(self, user=None): db.session.add(self) @@ -866,7 +865,7 @@ def are_favorites(cls, user, objects): if not objects: return [] - object_type = text_type(objects[0].__class__.__name__) + object_type = str(objects[0].__class__.__name__) return [ fav.object_id for fav in cls.query.filter( @@ -1305,7 +1304,7 @@ class NotificationDestination(BelongsToOrgMixin, db.Model): ) def __str__(self): - return text_type(self.name) + return str(self.name) def to_dict(self, all=False): d = { diff --git a/redash/models/parameterized_query.py b/redash/models/parameterized_query.py index 85225e0414..0094c0aa7d 100644 --- a/redash/models/parameterized_query.py +++ b/redash/models/parameterized_query.py @@ -6,15 +6,13 @@ from funcy import distinct from dateutil.parser import parse -from six import string_types, text_type - def _pluck_name_and_value(default_column, row): row = {k.lower(): v for k, v in row.items()} name_column = "name" if "name" in row.keys() else default_column.lower() value_column = "value" if "value" in row.keys() else default_column.lower() - return {"name": row[name_column], "value": text_type(row[value_column])} + return {"name": row[name_column], "value": str(row[value_column])} def _load_result(query_id, org): @@ -115,8 +113,8 @@ def _is_date_range(obj): def _is_value_within_options(value, dropdown_options, allow_list=False): if isinstance(value, list): - return allow_list and set(map(text_type, value)).issubset(set(dropdown_options)) - return text_type(value) in dropdown_options + return allow_list and set(map(str, value)).issubset(set(dropdown_options)) + return str(value) in dropdown_options class ParameterizedQuery(object): @@ -157,11 +155,11 @@ def _valid(self, name, value): query_id = definition.get("queryId") allow_multiple_values = isinstance(definition.get("multiValuesOptions"), dict) - if isinstance(enum_options, string_types): + if isinstance(enum_options, str): enum_options = enum_options.split("\n") validators = { - "text": lambda value: isinstance(value, string_types), + "text": lambda value: isinstance(value, str), "number": _is_number, "enum": lambda value: _is_value_within_options( value, enum_options, allow_multiple_values diff --git a/redash/models/users.py b/redash/models/users.py index 6be4882915..00325f4b0b 100644 --- a/redash/models/users.py +++ b/redash/models/users.py @@ -8,7 +8,6 @@ from flask import current_app as app, url_for, request_started from flask_login import current_user, AnonymousUserMixin, UserMixin from passlib.apps import custom_app_context as pwd_context -from six import string_types, text_type from sqlalchemy.exc import DBAPIError from sqlalchemy.dialects import postgresql @@ -290,7 +289,7 @@ class Group(db.Model, BelongsToOrgMixin): __tablename__ = "groups" def __str__(self): - return text_type(self.id) + return str(self.id) def to_dict(self): return { @@ -405,7 +404,7 @@ def is_api_user(self): class ApiUser(UserMixin, PermissionsCheckMixin): def __init__(self, api_key, org, groups, name=None): self.object = None - if isinstance(api_key, string_types): + if isinstance(api_key, str): self.id = api_key self.name = name else: diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index 061b325144..993f56c38d 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -3,8 +3,6 @@ from dateutil import parser import requests -from six import text_type - from redash import settings from redash.utils import json_loads @@ -292,7 +290,7 @@ def guess_type_from_string(string_value): except (ValueError, OverflowError): pass - if text_type(string_value).lower() in ("true", "false"): + if str(string_value).lower() in ("true", "false"): return TYPE_BOOLEAN try: diff --git a/redash/query_runner/couchbase.py b/redash/query_runner/couchbase.py index 4e1de63fd4..ec1740aac6 100644 --- a/redash/query_runner/couchbase.py +++ b/redash/query_runner/couchbase.py @@ -2,7 +2,6 @@ import logging from dateutil.parser import parse -from six import text_type from redash.query_runner import * from redash.utils import JSONEncoder, json_dumps, json_loads, parse_human_time @@ -18,7 +17,7 @@ TYPES_MAP = { str: TYPE_STRING, - text_type: TYPE_STRING, + bytes: TYPE_STRING, int: TYPE_INTEGER, float: TYPE_FLOAT, bool: TYPE_BOOLEAN, diff --git a/redash/query_runner/drill.py b/redash/query_runner/drill.py index d3b754374d..9111b2002d 100644 --- a/redash/query_runner/drill.py +++ b/redash/query_runner/drill.py @@ -4,8 +4,6 @@ from dateutil import parser -from six import text_type - from redash.query_runner import ( BaseHTTPQueryRunner, register, @@ -32,12 +30,12 @@ def convert_type(string_value, actual_type): return float(string_value) if actual_type == TYPE_BOOLEAN: - return text_type(string_value).lower() == "true" + return str(string_value).lower() == "true" if actual_type == TYPE_DATETIME: return parser.parse(string_value) - return text_type(string_value) + return str(string_value) # Parse Drill API response and translate it to accepted format diff --git a/redash/query_runner/elasticsearch.py b/redash/query_runner/elasticsearch.py index 04ec193049..6c26bcc211 100644 --- a/redash/query_runner/elasticsearch.py +++ b/redash/query_runner/elasticsearch.py @@ -6,7 +6,6 @@ import requests from requests.auth import HTTPBasicAuth -from six import string_types, text_type from redash.query_runner import * from redash.utils import json_dumps, json_loads @@ -35,7 +34,7 @@ PYTHON_TYPES_MAPPING = { str: TYPE_STRING, - text_type: TYPE_STRING, + bytes: TYPE_STRING, bool: TYPE_BOOLEAN, int: TYPE_INTEGER, float: TYPE_FLOAT, @@ -417,7 +416,7 @@ def run_query(self, query, user): result_columns = [] result_rows = [] - if isinstance(query_data, string_types): + if isinstance(query_data, str): _from = 0 while True: query_size = size if limit >= (_from + size) else (limit - _from) diff --git a/redash/query_runner/json_ds.py b/redash/query_runner/json_ds.py index 2a9cba3508..9b6095ba51 100644 --- a/redash/query_runner/json_ds.py +++ b/redash/query_runner/json_ds.py @@ -5,7 +5,6 @@ import datetime from urllib.parse import urlparse from funcy import compact, project -from six import text_type from redash.utils import json_dumps from redash.query_runner import ( BaseHTTPQueryRunner, @@ -32,19 +31,19 @@ def parse_query(query): return params except ValueError as e: logging.exception(e) - error = text_type(e) + error = str(e) raise QueryParseError(error) def is_private_address(url): hostname = urlparse(url).hostname ip_address = socket.gethostbyname(hostname) - return ipaddress.ip_address(text_type(ip_address)).is_private + return ipaddress.ip_address(str(ip_address)).is_private TYPES_MAP = { str: TYPE_STRING, - text_type: TYPE_STRING, + bytes: TYPE_STRING, int: TYPE_INTEGER, float: TYPE_FLOAT, bool: TYPE_BOOLEAN, diff --git a/redash/query_runner/mongodb.py b/redash/query_runner/mongodb.py index 6da92620e6..0639b02808 100644 --- a/redash/query_runner/mongodb.py +++ b/redash/query_runner/mongodb.py @@ -3,7 +3,6 @@ import re from dateutil.parser import parse -from six import string_types, text_type from redash.query_runner import * from redash.utils import JSONEncoder, json_dumps, json_loads, parse_human_time @@ -26,7 +25,7 @@ TYPES_MAP = { str: TYPE_STRING, - text_type: TYPE_STRING, + bytes: TYPE_STRING, int: TYPE_INTEGER, float: TYPE_FLOAT, bool: TYPE_BOOLEAN, @@ -57,7 +56,7 @@ def parse_oids(oids): def datetime_parser(dct): for k, v in dct.items(): - if isinstance(v, string_types): + if isinstance(v, str): m = date_regex.findall(v) if len(m) > 0: dct[k] = parse(m[0], yearfirst=True) diff --git a/redash/tasks/queries/execution.py b/redash/tasks/queries/execution.py index a725bf5ae8..a3f6f361ba 100644 --- a/redash/tasks/queries/execution.py +++ b/redash/tasks/queries/execution.py @@ -2,7 +2,6 @@ import signal import time import redis -from six import text_type from rq import get_current_job from rq.job import JobStatus @@ -162,7 +161,7 @@ def run(self): if isinstance(e, JobTimeoutException): error = TIMEOUT_MESSAGE else: - error = text_type(e) + error = str(e) data = None logging.warning("Unexpected error while running query:", exc_info=1) diff --git a/redash/utils/__init__.py b/redash/utils/__init__.py index 94f570f625..73a17d05e5 100644 --- a/redash/utils/__init__.py +++ b/redash/utils/__init__.py @@ -10,8 +10,6 @@ import uuid import binascii -from six import string_types - import pystache import pytz import simplejson @@ -148,7 +146,7 @@ def __init__(self, f, dialect=csv.excel, encoding=WRITER_ENCODING, **kwds): self.encoder = codecs.getincrementalencoder(encoding)() def _encode_utf8(self, val): - if isinstance(val, string_types): + if isinstance(val, str): return val.encode(WRITER_ENCODING, WRITER_ERRORS) return val diff --git a/requirements.txt b/requirements.txt index f9cf390747..e76671b9e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,7 +25,6 @@ pytz>=2019.3 PyYAML==5.1.2 redis==3.3.11 requests==2.22.0 -six==1.12.0 SQLAlchemy==1.3.10 # We can't upgrade SQLAlchemy-Searchable version as newer versions require PostgreSQL > 9.6, but we target older versions at the moment. SQLAlchemy-Searchable==0.10.6 diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 52fc173ff5..91be52ea76 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -1,8 +1,7 @@ +import importlib import os import time -from six.moves import reload_module - from flask import request from mock import patch from redash import models, settings @@ -328,11 +327,11 @@ def override_settings(self, overrides): variables = self.DEFAULT_SETTING_OVERRIDES.copy() variables.update(overrides or {}) with patch.dict(os.environ, variables): - reload_module(settings) + importlib.reload(settings) # Queue a cleanup routine that reloads the settings without overrides # once the test ends - self.addCleanup(lambda: reload_module(settings)) + self.addCleanup(lambda: importlib.reload(settings)) def assert_correct_user_attributes( self,