mirror of
https://github.com/apache/superset.git
synced 2026-04-18 15:44:57 +00:00
Re-enable pylint for some model files (#8770)
* Allow id as a valid name for pylint * Re-enable pylint for superset/models/core.py * re-enable pylint for superset/models/sql_lab.py * re-enable pylint for superset/models/schedules.py * re-enable pylint for superset/models/helpers.py * re-enable pylint for superset/models/annotations.py * re-enable pylint on superset/models/tags.py * a couple more fixes after black formatting... * Add another inline pylint disable * Fix black * Move to inline disables for 'id' attribute on models * Fix lint disables after black reformatted them
This commit is contained in:
committed by
Maxime Beauchemin
parent
6c130b32ad
commit
adf2cc2039
@@ -14,7 +14,6 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# pylint: disable=C,R,W
|
||||
"""a collection of Annotation-related models"""
|
||||
from flask_appbuilder import Model
|
||||
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String, Text
|
||||
@@ -28,7 +27,7 @@ class AnnotationLayer(Model, AuditMixinNullable):
|
||||
"""A logical namespace for a set of annotations"""
|
||||
|
||||
__tablename__ = "annotation_layer"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
name = Column(String(250))
|
||||
descr = Column(Text)
|
||||
|
||||
@@ -41,7 +40,7 @@ class Annotation(Model, AuditMixinNullable):
|
||||
"""Time-related annotation"""
|
||||
|
||||
__tablename__ = "annotation"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
start_dttm = Column(DateTime)
|
||||
end_dttm = Column(DateTime)
|
||||
layer_id = Column(Integer, ForeignKey("annotation_layer.id"), nullable=False)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# pylint: disable=C,R,W
|
||||
# pylint: disable=line-too-long,unused-argument,ungrouped-imports
|
||||
"""A collection of ORM sqlalchemy models for Superset"""
|
||||
import json
|
||||
import logging
|
||||
@@ -66,8 +66,9 @@ from superset.utils import cache as cache_util, core as utils
|
||||
from superset.viz import BaseViz, viz_types
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from superset.connectors.base.models import BaseDatasource
|
||||
|
||||
from superset.connectors.base.models import ( # pylint: disable=unused-import
|
||||
BaseDatasource,
|
||||
)
|
||||
|
||||
config = app.config
|
||||
custom_password_store = config["SQLALCHEMY_CUSTOM_PASSWORD_STORE"]
|
||||
@@ -76,6 +77,7 @@ log_query = config["QUERY_LOGGER"]
|
||||
metadata = Model.metadata # pylint: disable=no-member
|
||||
|
||||
PASSWORD_MASK = "X" * 10
|
||||
DB_CONNECTION_MUTATOR = config["DB_CONNECTION_MUTATOR"]
|
||||
|
||||
|
||||
def set_related_perm(mapper, connection, target):
|
||||
@@ -93,8 +95,8 @@ def copy_dashboard(mapper, connection, target):
|
||||
if dashboard_id is None:
|
||||
return
|
||||
|
||||
Session = sessionmaker(autoflush=False)
|
||||
session = Session(bind=connection)
|
||||
session_class = sessionmaker(autoflush=False)
|
||||
session = session_class(bind=connection)
|
||||
new_user = session.query(User).filter_by(id=target.id).first()
|
||||
|
||||
# copy template dashboard to user
|
||||
@@ -126,16 +128,16 @@ class Url(Model, AuditMixinNullable):
|
||||
"""Used for the short url feature"""
|
||||
|
||||
__tablename__ = "url"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
url = Column(Text)
|
||||
|
||||
|
||||
class KeyValue(Model):
|
||||
class KeyValue(Model): # pylint: disable=too-few-public-methods
|
||||
|
||||
"""Used for any type of key-value store"""
|
||||
|
||||
__tablename__ = "keyvalue"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
value = Column(Text, nullable=False)
|
||||
|
||||
|
||||
@@ -144,7 +146,7 @@ class CssTemplate(Model, AuditMixinNullable):
|
||||
"""CSS templates for dashboards"""
|
||||
|
||||
__tablename__ = "css_templates"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
template_name = Column(String(250))
|
||||
css = Column(Text, default="")
|
||||
|
||||
@@ -158,12 +160,14 @@ slice_user = Table(
|
||||
)
|
||||
|
||||
|
||||
class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
class Slice(
|
||||
Model, AuditMixinNullable, ImportMixin
|
||||
): # pylint: disable=too-many-public-methods
|
||||
|
||||
"""A slice is essentially a report or a view on data"""
|
||||
|
||||
__tablename__ = "slices"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
slice_name = Column(String(250))
|
||||
datasource_id = Column(Integer)
|
||||
datasource_type = Column(String(200))
|
||||
@@ -175,6 +179,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
perm = Column(String(1000))
|
||||
schema_perm = Column(String(1000))
|
||||
owners = relationship(security_manager.user_model, secondary=slice_user)
|
||||
token = ""
|
||||
|
||||
export_fields = [
|
||||
"slice_name",
|
||||
@@ -208,6 +213,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
cache_timeout=self.cache_timeout,
|
||||
)
|
||||
|
||||
# pylint: disable=using-constant-test
|
||||
@datasource.getter # type: ignore
|
||||
@utils.memoized
|
||||
def get_datasource(self) -> Optional["BaseDatasource"]:
|
||||
@@ -230,6 +236,8 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
datasource = self.datasource
|
||||
return datasource.url if datasource else None
|
||||
|
||||
# pylint: enable=using-constant-test
|
||||
|
||||
@property # type: ignore
|
||||
@utils.memoized
|
||||
def viz(self) -> BaseViz:
|
||||
@@ -249,7 +257,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
try:
|
||||
d = self.viz.data
|
||||
self.token = d.get("token") # type: ignore
|
||||
except Exception as e:
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
logging.exception(e)
|
||||
d["error"] = str(e)
|
||||
return {
|
||||
@@ -275,7 +283,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
form_data: Dict[str, Any] = {}
|
||||
try:
|
||||
form_data = json.loads(self.params)
|
||||
except Exception as e:
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
logging.error("Malformed json in slice's params")
|
||||
logging.exception(e)
|
||||
form_data.update(
|
||||
@@ -322,9 +330,8 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
|
||||
@property
|
||||
def slice_link(self) -> Markup:
|
||||
url = self.slice_url
|
||||
name = escape(self.chart)
|
||||
return Markup(f'<a href="{url}">{name}</a>')
|
||||
return Markup(f'<a href="{self.url}">{name}</a>')
|
||||
|
||||
def get_viz(self, force: bool = False) -> BaseViz:
|
||||
"""Creates :py:class:viz.BaseViz object from the url_params_multidict.
|
||||
@@ -392,7 +399,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
||||
session.flush()
|
||||
return slc_to_override.id
|
||||
session.add(slc_to_import)
|
||||
logging.info("Final slice: {}".format(slc_to_import.to_json()))
|
||||
logging.info("Final slice: %s", str(slc_to_import.to_json()))
|
||||
session.flush()
|
||||
return slc_to_import.id
|
||||
|
||||
@@ -423,12 +430,14 @@ dashboard_user = Table(
|
||||
)
|
||||
|
||||
|
||||
class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
class Dashboard( # pylint: disable=too-many-instance-attributes
|
||||
Model, AuditMixinNullable, ImportMixin
|
||||
):
|
||||
|
||||
"""The dashboard object!"""
|
||||
|
||||
__tablename__ = "dashboards"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
dashboard_title = Column(String(500))
|
||||
position_json = Column(utils.MediumText())
|
||||
description = Column(Text)
|
||||
@@ -470,7 +479,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
return "/superset/dashboard/{}/?preselect_filters={}".format(
|
||||
self.slug or self.id, filters
|
||||
)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
return f"/superset/dashboard/{self.slug or self.id}/"
|
||||
|
||||
@@ -485,8 +494,8 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
@property
|
||||
def sqla_metadata(self) -> None:
|
||||
# pylint: disable=no-member
|
||||
metadata = MetaData(bind=self.get_sqla_engine())
|
||||
metadata.reflect()
|
||||
meta = MetaData(bind=self.get_sqla_engine())
|
||||
meta.reflect()
|
||||
|
||||
def dashboard_link(self) -> Markup:
|
||||
title = escape(self.dashboard_title or "<empty>")
|
||||
@@ -523,7 +532,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
def import_obj(
|
||||
def import_obj( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
||||
cls, dashboard_to_import: "Dashboard", import_time: Optional[int] = None
|
||||
) -> int:
|
||||
"""Imports the dashboard from the object to the database.
|
||||
@@ -579,10 +588,10 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
dashboard.position_json = json.dumps(position_data)
|
||||
|
||||
logging.info(
|
||||
"Started import of the dashboard: {}".format(dashboard_to_import.to_json())
|
||||
"Started import of the dashboard: %s", dashboard_to_import.to_json()
|
||||
)
|
||||
session = db.session
|
||||
logging.info("Dashboard has {} slices".format(len(dashboard_to_import.slices)))
|
||||
logging.info("Dashboard has %d slices", len(dashboard_to_import.slices))
|
||||
# copy slices object as Slice.import_slice will mutate the slice
|
||||
# and will remove the existing dashboard - slice association
|
||||
slices = copy(dashboard_to_import.slices)
|
||||
@@ -599,9 +608,9 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
}
|
||||
for slc in slices:
|
||||
logging.info(
|
||||
"Importing slice {} from the dashboard: {}".format(
|
||||
slc.to_json(), dashboard_to_import.dashboard_title
|
||||
)
|
||||
"Importing slice %s from the dashboard: %s",
|
||||
slc.to_json(),
|
||||
dashboard_to_import.dashboard_title,
|
||||
)
|
||||
remote_slc = remote_id_slice_map.get(slc.id)
|
||||
new_slc_id = Slice.import_obj(slc, remote_slc, import_time=import_time)
|
||||
@@ -677,14 +686,16 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
existing_dashboard.slices = new_slices
|
||||
session.flush()
|
||||
return existing_dashboard.id
|
||||
else:
|
||||
dashboard_to_import.slices = new_slices
|
||||
session.add(dashboard_to_import)
|
||||
session.flush()
|
||||
return dashboard_to_import.id # type: ignore
|
||||
|
||||
dashboard_to_import.slices = new_slices
|
||||
session.add(dashboard_to_import)
|
||||
session.flush()
|
||||
return dashboard_to_import.id # type: ignore
|
||||
|
||||
@classmethod
|
||||
def export_dashboards(cls, dashboard_ids: List) -> str:
|
||||
def export_dashboards( # pylint: disable=too-many-locals
|
||||
cls, dashboard_ids: List
|
||||
) -> str:
|
||||
copied_dashboards = []
|
||||
datasource_ids = set()
|
||||
for dashboard_id in dashboard_ids:
|
||||
@@ -741,7 +752,9 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
||||
)
|
||||
|
||||
|
||||
class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
class Database(
|
||||
Model, AuditMixinNullable, ImportMixin
|
||||
): # pylint: disable=too-many-public-methods
|
||||
|
||||
"""An ORM object that stores Database related information"""
|
||||
|
||||
@@ -749,7 +762,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
type = "table"
|
||||
__table_args__ = (UniqueConstraint("database_name"),)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
verbose_name = Column(String(250), unique=True)
|
||||
# short unique name, used in permissions
|
||||
database_name = Column(String(250), unique=True, nullable=False)
|
||||
@@ -763,7 +776,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
allow_ctas = Column(Boolean, default=False)
|
||||
allow_dml = Column(Boolean, default=False)
|
||||
force_ctas_schema = Column(String(250))
|
||||
allow_multi_schema_metadata_fetch = Column(Boolean, default=False)
|
||||
allow_multi_schema_metadata_fetch = Column( # pylint: disable=invalid-name
|
||||
Boolean, default=False
|
||||
)
|
||||
extra = Column(
|
||||
Text,
|
||||
default=textwrap.dedent(
|
||||
@@ -836,8 +851,8 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
|
||||
@property
|
||||
def backend(self) -> str:
|
||||
url = make_url(self.sqlalchemy_uri_decrypted)
|
||||
return url.get_backend_name()
|
||||
sqlalchemy_url = make_url(self.sqlalchemy_uri_decrypted)
|
||||
return sqlalchemy_url.get_backend_name()
|
||||
|
||||
@property
|
||||
def metadata_cache_timeout(self) -> Dict[str, Any]:
|
||||
@@ -864,12 +879,14 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
return self.get_extra().get("default_schemas", [])
|
||||
|
||||
@classmethod
|
||||
def get_password_masked_url_from_uri(cls, uri: str):
|
||||
url = make_url(uri)
|
||||
return cls.get_password_masked_url(url)
|
||||
def get_password_masked_url_from_uri(cls, uri: str): # pylint: disable=invalid-name
|
||||
sqlalchemy_url = make_url(uri)
|
||||
return cls.get_password_masked_url(sqlalchemy_url)
|
||||
|
||||
@classmethod
|
||||
def get_password_masked_url(cls, url: URL) -> URL:
|
||||
def get_password_masked_url(
|
||||
cls, url: URL # pylint: disable=redefined-outer-name
|
||||
) -> URL:
|
||||
url_copy = deepcopy(url)
|
||||
if url_copy.password is not None:
|
||||
url_copy.password = PASSWORD_MASK
|
||||
@@ -884,7 +901,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
self.sqlalchemy_uri = str(conn) # hides the password
|
||||
|
||||
def get_effective_user(
|
||||
self, url: URL, user_name: Optional[str] = None
|
||||
self,
|
||||
url: URL, # pylint: disable=redefined-outer-name
|
||||
user_name: Optional[str] = None,
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Get the effective user, especially during impersonation.
|
||||
@@ -914,18 +933,18 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
source: Optional[int] = None,
|
||||
) -> Engine:
|
||||
extra = self.get_extra()
|
||||
url = make_url(self.sqlalchemy_uri_decrypted)
|
||||
url = self.db_engine_spec.adjust_database_uri(url, schema)
|
||||
effective_username = self.get_effective_user(url, user_name)
|
||||
sqlalchemy_url = make_url(self.sqlalchemy_uri_decrypted)
|
||||
sqlalchemy_url = self.db_engine_spec.adjust_database_uri(sqlalchemy_url, schema)
|
||||
effective_username = self.get_effective_user(sqlalchemy_url, user_name)
|
||||
# If using MySQL or Presto for example, will set url.username
|
||||
# If using Hive, will not do anything yet since that relies on a
|
||||
# configuration parameter instead.
|
||||
self.db_engine_spec.modify_url_for_impersonation(
|
||||
url, self.impersonate_user, effective_username
|
||||
sqlalchemy_url, self.impersonate_user, effective_username
|
||||
)
|
||||
|
||||
masked_url = self.get_password_masked_url(url)
|
||||
logging.info("Database.get_sqla_engine(). Masked URL: {0}".format(masked_url))
|
||||
masked_url = self.get_password_masked_url(sqlalchemy_url)
|
||||
logging.info("Database.get_sqla_engine(). Masked URL: %s", str(masked_url))
|
||||
|
||||
params = extra.get("engine_params", {})
|
||||
if nullpool:
|
||||
@@ -935,7 +954,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
configuration: Dict[str, Any] = {}
|
||||
configuration.update(
|
||||
self.db_engine_spec.get_configuration_for_impersonation(
|
||||
str(url), self.impersonate_user, effective_username
|
||||
str(sqlalchemy_url), self.impersonate_user, effective_username
|
||||
)
|
||||
)
|
||||
if configuration:
|
||||
@@ -945,12 +964,11 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
|
||||
params.update(self.get_encrypted_extra())
|
||||
|
||||
DB_CONNECTION_MUTATOR = config["DB_CONNECTION_MUTATOR"]
|
||||
if DB_CONNECTION_MUTATOR:
|
||||
url, params = DB_CONNECTION_MUTATOR(
|
||||
url, params, effective_username, security_manager, source
|
||||
sqlalchemy_url, params = DB_CONNECTION_MUTATOR(
|
||||
sqlalchemy_url, params, effective_username, security_manager, source
|
||||
)
|
||||
return create_engine(url, **params)
|
||||
return create_engine(sqlalchemy_url, **params)
|
||||
|
||||
def get_reserved_words(self) -> Set[str]:
|
||||
return self.get_dialect().preparer.reserved_words
|
||||
@@ -958,7 +976,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
def get_quoter(self):
|
||||
return self.get_dialect().identifier_preparer.quote
|
||||
|
||||
def get_df(
|
||||
def get_df( # pylint: disable=too-many-locals
|
||||
self, sql: str, schema: str, mutator: Optional[Callable] = None
|
||||
) -> pd.DataFrame:
|
||||
sqls = [str(s).strip(" ;") for s in sqlparse.parse(sql)]
|
||||
@@ -982,9 +1000,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
|
||||
with closing(engine.raw_connection()) as conn:
|
||||
with closing(conn.cursor()) as cursor:
|
||||
for sql in sqls[:-1]:
|
||||
_log_query(sql)
|
||||
self.db_engine_spec.execute(cursor, sql)
|
||||
for sql_ in sqls[:-1]:
|
||||
_log_query(sql_)
|
||||
self.db_engine_spec.execute(cursor, sql_)
|
||||
cursor.fetchall()
|
||||
|
||||
_log_query(sqls[-1])
|
||||
@@ -1012,12 +1030,14 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
|
||||
sql = str(qry.compile(engine, compile_kwargs={"literal_binds": True}))
|
||||
|
||||
if engine.dialect.identifier_preparer._double_percents:
|
||||
if (
|
||||
engine.dialect.identifier_preparer._double_percents # pylint: disable=protected-access
|
||||
):
|
||||
sql = sql.replace("%%", "%")
|
||||
|
||||
return sql
|
||||
|
||||
def select_star(
|
||||
def select_star( # pylint: disable=too-many-arguments
|
||||
self,
|
||||
table_name: str,
|
||||
sql: Optional[str] = None,
|
||||
@@ -1109,7 +1129,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
return [
|
||||
utils.DatasourceName(table=table, schema=schema) for table in tables
|
||||
]
|
||||
except Exception as e:
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
logging.exception(e)
|
||||
|
||||
@cache_util.memoized_func(
|
||||
@@ -1139,7 +1159,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
database=self, inspector=self.inspector, schema=schema
|
||||
)
|
||||
return [utils.DatasourceName(table=view, schema=schema) for view in views]
|
||||
except Exception as e:
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
logging.exception(e)
|
||||
|
||||
@cache_util.memoized_func(
|
||||
@@ -1232,7 +1252,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
||||
) -> List[Dict[str, Any]]:
|
||||
return self.inspector.get_foreign_keys(table_name, schema)
|
||||
|
||||
def get_schema_access_for_csv_upload(self) -> List[str]:
|
||||
def get_schema_access_for_csv_upload( # pylint: disable=invalid-name
|
||||
self
|
||||
) -> List[str]:
|
||||
return self.get_extra().get("schemas_allowed_for_csv_upload", [])
|
||||
|
||||
@property
|
||||
@@ -1269,13 +1291,13 @@ sqla.event.listen(Database, "after_insert", security_manager.set_perm)
|
||||
sqla.event.listen(Database, "after_update", security_manager.set_perm)
|
||||
|
||||
|
||||
class Log(Model):
|
||||
class Log(Model): # pylint: disable=too-few-public-methods
|
||||
|
||||
"""ORM object used to log Superset actions to the database"""
|
||||
|
||||
__tablename__ = "logs"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
action = Column(String(512))
|
||||
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
||||
dashboard_id = Column(Integer)
|
||||
@@ -1289,10 +1311,10 @@ class Log(Model):
|
||||
referrer = Column(String(1024))
|
||||
|
||||
|
||||
class FavStar(Model):
|
||||
class FavStar(Model): # pylint: disable=too-few-public-methods
|
||||
__tablename__ = "favstar"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
||||
class_name = Column(String(50))
|
||||
obj_id = Column(Integer)
|
||||
@@ -1303,7 +1325,7 @@ class DatasourceAccessRequest(Model, AuditMixinNullable):
|
||||
"""ORM model for the access requests for datasources and dbs."""
|
||||
|
||||
__tablename__ = "access_request"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
|
||||
datasource_id = Column(Integer)
|
||||
datasource_type = Column(String(200))
|
||||
@@ -1337,33 +1359,33 @@ class DatasourceAccessRequest(Model, AuditMixinNullable):
|
||||
action_list = ""
|
||||
perm = self.datasource.perm # pylint: disable=no-member
|
||||
pv = security_manager.find_permission_view_menu("datasource_access", perm)
|
||||
for r in pv.role:
|
||||
if r.name in self.ROLES_BLACKLIST:
|
||||
for role in pv.role:
|
||||
if role.name in self.ROLES_BLACKLIST:
|
||||
continue
|
||||
# pylint: disable=no-member
|
||||
url = (
|
||||
href = (
|
||||
f"/superset/approve?datasource_type={self.datasource_type}&"
|
||||
f"datasource_id={self.datasource_id}&"
|
||||
f"created_by={self.created_by.username}&role_to_grant={r.name}"
|
||||
f"created_by={self.created_by.username}&role_to_grant={role.name}"
|
||||
)
|
||||
href = '<a href="{}">Grant {} Role</a>'.format(url, r.name)
|
||||
action_list = action_list + "<li>" + href + "</li>"
|
||||
link = '<a href="{}">Grant {} Role</a>'.format(href, role.name)
|
||||
action_list = action_list + "<li>" + link + "</li>"
|
||||
return "<ul>" + action_list + "</ul>"
|
||||
|
||||
@property
|
||||
def user_roles(self) -> str:
|
||||
action_list = ""
|
||||
for r in self.created_by.roles: # pylint: disable=no-member
|
||||
for role in self.created_by.roles: # pylint: disable=no-member
|
||||
# pylint: disable=no-member
|
||||
url = (
|
||||
href = (
|
||||
f"/superset/approve?datasource_type={self.datasource_type}&"
|
||||
f"datasource_id={self.datasource_id}&"
|
||||
f"created_by={self.created_by.username}&role_to_extend={r.name}"
|
||||
f"created_by={self.created_by.username}&role_to_extend={role.name}"
|
||||
)
|
||||
href = '<a href="{}">Extend {} Role</a>'.format(url, r.name)
|
||||
if r.name in self.ROLES_BLACKLIST:
|
||||
href = "{} Role".format(r.name)
|
||||
action_list = action_list + "<li>" + href + "</li>"
|
||||
link = '<a href="{}">Extend {} Role</a>'.format(href, role.name)
|
||||
if role.name in self.ROLES_BLACKLIST:
|
||||
link = "{} Role".format(role.name)
|
||||
action_list = action_list + "<li>" + link + "</li>"
|
||||
return "<ul>" + action_list + "</ul>"
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# pylint: disable=C,R,W
|
||||
"""a collection of model-related helper classes and functions"""
|
||||
import json
|
||||
import logging
|
||||
@@ -22,6 +21,8 @@ import re
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
# isort and pylint disagree, isort should win
|
||||
# pylint: disable=ungrouped-imports
|
||||
import humanize
|
||||
import sqlalchemy as sa
|
||||
import yaml
|
||||
@@ -38,10 +39,12 @@ from superset.utils.core import QueryStatus
|
||||
def json_to_dict(json_str):
|
||||
if json_str:
|
||||
val = re.sub(",[ \t\r\n]+}", "}", json_str)
|
||||
val = re.sub(",[ \t\r\n]+\]", "]", val)
|
||||
val = re.sub(
|
||||
",[ \t\r\n]+\]", "]", val # pylint: disable=anomalous-backslash-in-string
|
||||
)
|
||||
return json.loads(val)
|
||||
else:
|
||||
return {}
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
class ImportMixin(object):
|
||||
@@ -83,24 +86,24 @@ class ImportMixin(object):
|
||||
if not include_parent_ref:
|
||||
parent_ref = cls.__mapper__.relationships.get(cls.export_parent)
|
||||
if parent_ref:
|
||||
parent_excludes = {c.name for c in parent_ref.local_columns}
|
||||
parent_excludes = {column.name for column in parent_ref.local_columns}
|
||||
|
||||
def formatter(c):
|
||||
def formatter(column):
|
||||
return (
|
||||
"{0} Default ({1})".format(str(c.type), c.default.arg)
|
||||
if c.default
|
||||
else str(c.type)
|
||||
"{0} Default ({1})".format(str(column.type), column.default.arg)
|
||||
if column.default
|
||||
else str(column.type)
|
||||
)
|
||||
|
||||
schema = {
|
||||
c.name: formatter(c)
|
||||
for c in cls.__table__.columns
|
||||
if (c.name in cls.export_fields and c.name not in parent_excludes)
|
||||
column.name: formatter(column)
|
||||
for column in cls.__table__.columns
|
||||
if (column.name in cls.export_fields and column.name not in parent_excludes)
|
||||
}
|
||||
if recursive:
|
||||
for c in cls.export_children:
|
||||
child_class = cls.__mapper__.relationships[c].argument.class_
|
||||
schema[c] = [
|
||||
for column in cls.export_children:
|
||||
child_class = cls.__mapper__.relationships[column].argument.class_
|
||||
schema[column] = [
|
||||
child_class.export_schema(
|
||||
recursive=recursive, include_parent_ref=include_parent_ref
|
||||
)
|
||||
@@ -108,8 +111,12 @@ class ImportMixin(object):
|
||||
return schema
|
||||
|
||||
@classmethod
|
||||
def import_from_dict(cls, session, dict_rep, parent=None, recursive=True, sync=[]):
|
||||
def import_from_dict(
|
||||
cls, session, dict_rep, parent=None, recursive=True, sync=None
|
||||
): # pylint: disable=too-many-arguments,too-many-locals,too-many-branches
|
||||
"""Import obj from a dictionary"""
|
||||
if sync is None:
|
||||
sync = []
|
||||
parent_refs = cls._parent_foreign_key_mappings()
|
||||
export_fields = set(cls.export_fields) | set(parent_refs.keys())
|
||||
new_children = {
|
||||
@@ -126,10 +133,10 @@ class ImportMixin(object):
|
||||
|
||||
if not parent:
|
||||
if cls.export_parent:
|
||||
for p in parent_refs.keys():
|
||||
if p not in dict_rep:
|
||||
for prnt in parent_refs.keys():
|
||||
if prnt not in dict_rep:
|
||||
raise RuntimeError(
|
||||
"{0}: Missing field {1}".format(cls.__name__, p)
|
||||
"{0}: Missing field {1}".format(cls.__name__, prnt)
|
||||
)
|
||||
else:
|
||||
# Set foreign keys to parent obj
|
||||
@@ -182,10 +189,10 @@ class ImportMixin(object):
|
||||
|
||||
# Recursively create children
|
||||
if recursive:
|
||||
for c in cls.export_children:
|
||||
child_class = cls.__mapper__.relationships[c].argument.class_
|
||||
for child in cls.export_children:
|
||||
child_class = cls.__mapper__.relationships[child].argument.class_
|
||||
added = []
|
||||
for c_obj in new_children.get(c, []):
|
||||
for c_obj in new_children.get(child, []):
|
||||
added.append(
|
||||
child_class.import_from_dict(
|
||||
session=session, dict_rep=c_obj, parent=obj, sync=sync
|
||||
@@ -193,8 +200,10 @@ class ImportMixin(object):
|
||||
)
|
||||
# If children should get synced, delete the ones that did not
|
||||
# get updated.
|
||||
if c in sync and not is_new_obj:
|
||||
back_refs = child_class._parent_foreign_key_mappings()
|
||||
if child in sync and not is_new_obj:
|
||||
back_refs = (
|
||||
child_class._parent_foreign_key_mappings() # pylint: disable=protected-access
|
||||
)
|
||||
delete_filters = [
|
||||
getattr(child_class, k) == getattr(obj, back_refs.get(k))
|
||||
for k in back_refs.keys()
|
||||
@@ -203,7 +212,7 @@ class ImportMixin(object):
|
||||
session.query(child_class).filter(and_(*delete_filters))
|
||||
).difference(set(added))
|
||||
for o in to_delete:
|
||||
logging.info("Deleting %s %s", c, str(obj))
|
||||
logging.info("Deleting %s %s", child, str(obj))
|
||||
session.delete(o)
|
||||
|
||||
return obj
|
||||
@@ -234,16 +243,16 @@ class ImportMixin(object):
|
||||
)
|
||||
}
|
||||
if recursive:
|
||||
for c in self.export_children:
|
||||
for cld in self.export_children:
|
||||
# sorting to make lists of children stable
|
||||
dict_rep[c] = sorted(
|
||||
dict_rep[cld] = sorted(
|
||||
[
|
||||
child.export_to_dict(
|
||||
recursive=recursive,
|
||||
include_parent_ref=include_parent_ref,
|
||||
include_defaults=include_defaults,
|
||||
)
|
||||
for child in getattr(self, c)
|
||||
for child in getattr(self, cld)
|
||||
],
|
||||
key=lambda k: sorted(str(k.items())),
|
||||
)
|
||||
@@ -276,7 +285,7 @@ class ImportMixin(object):
|
||||
try:
|
||||
if g.user:
|
||||
self.owners = [g.user]
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
self.owners = []
|
||||
|
||||
@property
|
||||
@@ -288,6 +297,13 @@ class ImportMixin(object):
|
||||
return json_to_dict(self.template_params)
|
||||
|
||||
|
||||
def _user_link(user): # pylint: disable=no-self-use
|
||||
if not user:
|
||||
return ""
|
||||
url = "/superset/profile/{}/".format(user.username)
|
||||
return Markup('<a href="{}">{}</a>'.format(url, escape(user) or ""))
|
||||
|
||||
|
||||
class AuditMixinNullable(AuditMixin):
|
||||
|
||||
"""Altering the AuditMixin to use nullable fields
|
||||
@@ -319,12 +335,6 @@ class AuditMixinNullable(AuditMixin):
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
def _user_link(self, user):
|
||||
if not user:
|
||||
return ""
|
||||
url = "/superset/profile/{}/".format(user.username)
|
||||
return Markup('<a href="{}">{}</a>'.format(url, escape(user) or ""))
|
||||
|
||||
def changed_by_name(self):
|
||||
if self.created_by:
|
||||
return escape("{}".format(self.created_by))
|
||||
@@ -332,11 +342,11 @@ class AuditMixinNullable(AuditMixin):
|
||||
|
||||
@renders("created_by")
|
||||
def creator(self):
|
||||
return self._user_link(self.created_by)
|
||||
return _user_link(self.created_by)
|
||||
|
||||
@property
|
||||
def changed_by_(self):
|
||||
return self._user_link(self.changed_by)
|
||||
return _user_link(self.changed_by)
|
||||
|
||||
@renders("changed_on")
|
||||
def changed_on_(self):
|
||||
@@ -351,14 +361,14 @@ class AuditMixinNullable(AuditMixin):
|
||||
return Markup(f'<span class="no-wrap">{self.changed_on_humanized}</span>')
|
||||
|
||||
|
||||
class QueryResult(object):
|
||||
class QueryResult(object): # pylint: disable=too-few-public-methods
|
||||
|
||||
"""Object returned by the query interface"""
|
||||
|
||||
def __init__(
|
||||
def __init__( # pylint: disable=too-many-arguments
|
||||
self, df, query, duration, status=QueryStatus.SUCCESS, error_message=None
|
||||
):
|
||||
self.df = df
|
||||
self.df = df # pylint: disable=invalid-name
|
||||
self.query = query
|
||||
self.duration = duration
|
||||
self.status = status
|
||||
@@ -374,7 +384,7 @@ class ExtraJSONMixin:
|
||||
def extra(self):
|
||||
try:
|
||||
return json.loads(self.extra_json)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return {}
|
||||
|
||||
def set_extra_json(self, d):
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# pylint: disable=C,R,W
|
||||
"""Models for scheduled execution of jobs"""
|
||||
|
||||
import enum
|
||||
@@ -51,7 +50,7 @@ class EmailSchedule:
|
||||
|
||||
__tablename__ = "email_schedules"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
active = Column(Boolean, default=True, index=True)
|
||||
crontab = Column(String(50))
|
||||
|
||||
@@ -92,3 +91,4 @@ def get_scheduler_model(report_type):
|
||||
return DashboardEmailSchedule
|
||||
elif report_type == ScheduleType.slice.value:
|
||||
return SliceEmailSchedule
|
||||
return None
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# pylint: disable=C,R,W
|
||||
"""A collection of ORM sqlalchemy models for SQL Lab"""
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
# pylint: disable=ungrouped-imports
|
||||
import simplejson as json
|
||||
import sqlalchemy as sqla
|
||||
from flask import Markup
|
||||
@@ -48,7 +48,7 @@ class Query(Model, ExtraJSONMixin):
|
||||
table may represent multiple SQL statements executed sequentially"""
|
||||
|
||||
__tablename__ = "query"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
client_id = Column(String(11), unique=True, nullable=False)
|
||||
|
||||
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)
|
||||
@@ -149,7 +149,7 @@ class SavedQuery(Model, AuditMixinNullable, ExtraJSONMixin):
|
||||
"""ORM model for SQL query"""
|
||||
|
||||
__tablename__ = "saved_query"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
user_id = Column(Integer, ForeignKey("ab_user.id"), nullable=True)
|
||||
db_id = Column(Integer, ForeignKey("dbs.id"), nullable=True)
|
||||
schema = Column(String(128))
|
||||
@@ -194,7 +194,9 @@ class TabState(Model, AuditMixinNullable, ExtraJSONMixin):
|
||||
__tablename__ = "tab_state"
|
||||
|
||||
# basic info
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
id = Column( # pylint: disable=invalid-name
|
||||
Integer, primary_key=True, autoincrement=True
|
||||
)
|
||||
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
||||
label = Column(String(256))
|
||||
active = Column(Boolean, default=False)
|
||||
@@ -245,7 +247,9 @@ class TableSchema(Model, AuditMixinNullable, ExtraJSONMixin):
|
||||
|
||||
__tablename__ = "table_schema"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
id = Column( # pylint: disable=invalid-name
|
||||
Integer, primary_key=True, autoincrement=True
|
||||
)
|
||||
tab_state_id = Column(Integer, ForeignKey("tab_state.id", ondelete="CASCADE"))
|
||||
|
||||
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# pylint: disable=C,R,W,no-init
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import enum
|
||||
|
||||
Reference in New Issue
Block a user