mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-01 12:18:12 +08:00
chore: upgrade backend dependencies (#2641)
chore: upgrade backend dependencies (#2641)
This commit is contained in:
+3
-3
@@ -48,12 +48,12 @@ def _cache_control(always, **cache_kwargs):
|
||||
|
||||
|
||||
def cache_control(**cache_kwargs):
|
||||
""" config driven """
|
||||
"""config driven"""
|
||||
return _cache_control(False, **cache_kwargs)
|
||||
|
||||
|
||||
def cache_control_always(**cache_kwargs):
|
||||
""" always generate headers, regardless of the config """
|
||||
"""always generate headers, regardless of the config"""
|
||||
return _cache_control(True, **cache_kwargs)
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ def get_api_dataroot_resources(bp_dataroot):
|
||||
class Server:
|
||||
@staticmethod
|
||||
def _before_adding_routes(app, app_config):
|
||||
""" will be called before routes are added, during __init__. Subclass protocol """
|
||||
"""will be called before routes are added, during __init__. Subclass protocol"""
|
||||
pass
|
||||
|
||||
def __init__(self, app_config):
|
||||
|
||||
@@ -6,7 +6,7 @@ CXGUID = "cxguid"
|
||||
|
||||
|
||||
def get_user_id(session: SessionMixin) -> str:
|
||||
""" Gets a session-persistent user id. Creates one in the Flask session if non-extant """
|
||||
"""Gets a session-persistent user id. Creates one in the Flask session if non-extant"""
|
||||
if CXGUID not in session:
|
||||
session[CXGUID] = uuid4().hex
|
||||
session.permanent = True
|
||||
|
||||
@@ -26,9 +26,7 @@ def annotate_args(func):
|
||||
|
||||
|
||||
@sort_options
|
||||
@click.command(
|
||||
options_metavar="<options>"
|
||||
)
|
||||
@click.command(options_metavar="<options>")
|
||||
@click.argument(
|
||||
"input_h5ad_file",
|
||||
type=click.Path(exists=True, dir_okay=False, readable=True),
|
||||
@@ -51,8 +49,8 @@ def annotate_args(func):
|
||||
"--output-h5ad-file",
|
||||
default="",
|
||||
help="The output H5AD file that will contain the generated annotation values. If this option is not provided, "
|
||||
"the input file will be overwritten to include the new annotations; in this case you must specify "
|
||||
"--overwrite.",
|
||||
"the input file will be overwritten to include the new annotations; in this case you must specify "
|
||||
"--overwrite.",
|
||||
metavar="<filename>",
|
||||
)
|
||||
@click.option(
|
||||
@@ -60,7 +58,7 @@ def annotate_args(func):
|
||||
default=False,
|
||||
is_flag=True,
|
||||
help="Allow overwriting of the specified H5AD output file, if it exists. For safety, you must specify this "
|
||||
"flag if the specified output file already exists or if the --output-h5ad-file option is not provided.",
|
||||
"flag if the specified output file already exists or if the --output-h5ad-file option is not provided.",
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
|
||||
@@ -145,7 +145,7 @@ class AnnotationsLocalFile(Annotations):
|
||||
def write_gene_sets(self, gene_sets, tid, data_adaptor):
|
||||
self.check_gene_sets_save_enabled() # raises
|
||||
|
||||
if type(tid) != int or tid < 0:
|
||||
if type(tid) is not int or tid < 0:
|
||||
raise ValueError("tid must be a positive integer")
|
||||
|
||||
# may raise
|
||||
@@ -175,7 +175,7 @@ class AnnotationsLocalFile(Annotations):
|
||||
|
||||
# update the cache
|
||||
self.last_geneset_fname = fname
|
||||
self.last_geneset = gene_sets if type(gene_sets) == dict else {g["geneset_name"]: g for g in gene_sets}
|
||||
self.last_geneset = gene_sets if isinstance(gene_sets, dict) else {g["geneset_name"]: g for g in gene_sets}
|
||||
|
||||
def _get_userdata_idhash(self, data_adaptor):
|
||||
"""
|
||||
|
||||
@@ -56,7 +56,7 @@ def diffexp_ttest_from_mean_var(meanA, varA, nA, meanB, varB, nB, top_n, diffexp
|
||||
|
||||
# degrees of freedom for Welch's t-test
|
||||
with np.errstate(divide="ignore", invalid="ignore"):
|
||||
dof = sum_vn ** 2 / (vnA ** 2 / (nA - 1) + vnB ** 2 / (nB - 1))
|
||||
dof = sum_vn**2 / (vnA**2 / (nA - 1) + vnB**2 / (nB - 1))
|
||||
dof[np.isnan(dof)] = 1
|
||||
|
||||
# Welch's t-test score calculation
|
||||
|
||||
@@ -97,7 +97,7 @@ def estimate_approximate_distribution(X) -> XApproximateDistribution:
|
||||
if Xdata.size > CHUNKSIZE:
|
||||
min_val = max_val = Xdata[0]
|
||||
with concurrent.futures.ThreadPoolExecutor() as tp:
|
||||
for (_min, _max) in tp.map(min_max, [Xdata[i : i + CHUNKSIZE] for i in range(0, Xdata.size, CHUNKSIZE)]):
|
||||
for _min, _max in tp.map(min_max, [Xdata[i : i + CHUNKSIZE] for i in range(0, Xdata.size, CHUNKSIZE)]):
|
||||
min_val = min(_min, min_val)
|
||||
max_val = max(_max, max_val)
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
DEFAULT_SERVER_PORT = 5005
|
||||
BIG_FILE_SIZE_THRESHOLD = 100 * 2 ** 20 # 100MB
|
||||
BIG_FILE_SIZE_THRESHOLD = 100 * 2**20 # 100MB
|
||||
|
||||
@@ -19,7 +19,6 @@ class AppConfig(object):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# the default configuration (see default_config.py)
|
||||
# TODO @madison -- if we always read from the default config (hard coded path) can we set those values as
|
||||
# defaults within the config class?
|
||||
|
||||
@@ -50,7 +50,7 @@ class BaseConfig(object):
|
||||
f"Invalid type for attribute: {attrname}, expected types ({tnames}), got {type(val).__name__}"
|
||||
)
|
||||
else:
|
||||
if type(val) != vtype:
|
||||
if type(val) is not vtype:
|
||||
raise ConfigurationError(
|
||||
f"Invalid type for attribute: {attrname}, "
|
||||
f"expected type {vtype.__name__}, got {type(val).__name__}"
|
||||
@@ -70,7 +70,7 @@ class BaseConfig(object):
|
||||
if not hasattr(self, key):
|
||||
raise ConfigurationError(f"unknown config parameter {key}.")
|
||||
try:
|
||||
if type(value) == tuple:
|
||||
if type(value) is tuple:
|
||||
# convert tuple values to list values
|
||||
value = list(value)
|
||||
setattr(self, key, value)
|
||||
|
||||
@@ -29,7 +29,7 @@ class ExternalConfig(BaseConfig):
|
||||
if name is None:
|
||||
raise ConfigurationError("environment: 'name' is missing")
|
||||
required = envdict.get("required", False)
|
||||
if type(required) != bool:
|
||||
if type(required) is not bool:
|
||||
raise ConfigurationError("environment: 'required' must be a bool")
|
||||
path = envdict.get("path")
|
||||
if path is None:
|
||||
|
||||
@@ -19,7 +19,7 @@ import server.common.fbs.NetEncoding.Uint32Array as Uint32Array
|
||||
|
||||
# Serialization helper
|
||||
def serialize_column(builder, typed_arr):
|
||||
""" Serialize NetEncoding.Column """
|
||||
"""Serialize NetEncoding.Column"""
|
||||
|
||||
(u_type, u_value) = typed_arr
|
||||
Column.ColumnStart(builder)
|
||||
@@ -30,7 +30,7 @@ def serialize_column(builder, typed_arr):
|
||||
|
||||
# Serialization helper
|
||||
def serialize_matrix(builder, n_rows, n_cols, columns, col_idx):
|
||||
""" Serialize NetEncoding.Matrix """
|
||||
"""Serialize NetEncoding.Matrix"""
|
||||
|
||||
Matrix.MatrixStart(builder)
|
||||
Matrix.MatrixAddNRows(builder, n_rows)
|
||||
|
||||
@@ -136,7 +136,7 @@ def write_gene_sets_tidycsv(f, genesets):
|
||||
|
||||
|
||||
def summarizeQueryHash(raw_query):
|
||||
""" generate a cache key (hash) from the raw query string """
|
||||
"""generate a cache key (hash) from the raw query string"""
|
||||
return hashlib.sha1(raw_query).hexdigest()
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ def validate_gene_sets(genesets, var_names, context=None):
|
||||
# 1. check gene set character set and format
|
||||
illegal_name = re.compile(r"^\s| |[\u0000-\u001F\u007F-\uFFFF]|\s$")
|
||||
for name in geneset_names:
|
||||
if type(name) != str or len(name) == 0:
|
||||
if type(name) is not str or len(name) == 0:
|
||||
raise KeyError("Gene set names must be non-null string.")
|
||||
if illegal_name.search(name):
|
||||
messagefn(
|
||||
|
||||
@@ -6,7 +6,7 @@ import zlib
|
||||
import json
|
||||
|
||||
from flask import make_response, jsonify, current_app, abort
|
||||
from werkzeug.urls import url_unquote
|
||||
from urllib.parse import unquote
|
||||
|
||||
from server.common.config.client_config import get_client_config
|
||||
from server.common.constants import Axis, DiffExpMode, JSON_NaN_to_num_warning_msg
|
||||
@@ -64,22 +64,22 @@ def _query_parameter_to_filter(args):
|
||||
axis, name = key.split(":")
|
||||
if axis not in ("obs", "var"):
|
||||
raise FilterError("unknown filter axis")
|
||||
name = url_unquote(name)
|
||||
name = unquote(name)
|
||||
current = filters[axis].setdefault(name, {"name": name})
|
||||
|
||||
val_split = value.split(",")
|
||||
if len(val_split) == 1:
|
||||
if "min" in current or "max" in current:
|
||||
raise FilterError("do not mix range and value filters")
|
||||
value = url_unquote(value)
|
||||
value = unquote(value)
|
||||
values = current.setdefault("values", [])
|
||||
values.append(value)
|
||||
|
||||
elif len(val_split) == 2:
|
||||
if len(current) > 1:
|
||||
raise FilterError("duplicate range specification")
|
||||
min = url_unquote(val_split[0])
|
||||
max = url_unquote(val_split[1])
|
||||
min = unquote(val_split[0])
|
||||
max = unquote(val_split[1])
|
||||
if min != "*":
|
||||
current["min"] = float(min)
|
||||
if max != "*":
|
||||
@@ -379,7 +379,7 @@ def summarize_var_helper(request, data_adaptor, key, raw_query):
|
||||
HTTPStatus.OK,
|
||||
{"Content-Type": "application/octet-stream"},
|
||||
)
|
||||
except (ValueError) as e:
|
||||
except ValueError as e:
|
||||
return abort(HTTPStatus.NOT_FOUND, description=str(e))
|
||||
except (UnsupportedSummaryMethod, FilterError) as e:
|
||||
return abort(HTTPStatus.BAD_REQUEST, description=str(e))
|
||||
|
||||
@@ -8,7 +8,7 @@ import socket
|
||||
from urllib.parse import urlsplit, urljoin
|
||||
|
||||
import numpy as np
|
||||
from flask import json
|
||||
import json
|
||||
|
||||
from server.common.errors import ConfigurationError
|
||||
|
||||
@@ -100,6 +100,7 @@ def custom_format_warning(msg, *args, **kwargs):
|
||||
def jsonify_strict(data):
|
||||
return StrictJSONEncoder().encode(data)
|
||||
|
||||
|
||||
def import_plugins(plugin_module):
|
||||
"""
|
||||
Load optional plugin modules from server.common.plugins
|
||||
|
||||
@@ -92,7 +92,7 @@ class AnndataAdaptor(DataAdaptor):
|
||||
"""
|
||||
self.original_obs_index = self.data.obs.index
|
||||
|
||||
for (ax_name, var_name) in ((Axis.OBS, "obs"), (Axis.VAR, "var")):
|
||||
for ax_name, var_name in ((Axis.OBS, "obs"), (Axis.VAR, "var")):
|
||||
config_name = f"single_dataset__{var_name}_names"
|
||||
parameter_name = f"{var_name}_names"
|
||||
name = getattr(self.server_config, config_name)
|
||||
@@ -175,10 +175,11 @@ class AnndataAdaptor(DataAdaptor):
|
||||
raise DatasetAccessError("Out of memory - file is too large for available memory.")
|
||||
except Exception:
|
||||
import traceback
|
||||
|
||||
message = (
|
||||
"File not found or is inaccessible. File must be an .h5ad object. "
|
||||
"Please check your input and try again."
|
||||
)
|
||||
)
|
||||
if self.server_config.app__verbose:
|
||||
message += f"\n{traceback.format_exc()}"
|
||||
raise DatasetAccessError(message)
|
||||
@@ -218,7 +219,7 @@ class AnndataAdaptor(DataAdaptor):
|
||||
* with shape (n_obs, >= 2)
|
||||
* with all values finite or NaN (no +Inf or -Inf)
|
||||
"""
|
||||
is_valid = type(arr) == np.ndarray and arr.dtype.kind in "fiu"
|
||||
is_valid = type(arr) is np.ndarray and arr.dtype.kind in "fiu"
|
||||
is_valid = is_valid and arr.shape[0] == self.data.n_obs and arr.shape[1] >= 2
|
||||
is_valid = is_valid and not np.any(np.isinf(arr)) and not np.all(np.isnan(arr))
|
||||
return is_valid
|
||||
@@ -242,8 +243,10 @@ class AnndataAdaptor(DataAdaptor):
|
||||
)
|
||||
if self.data.X.dtype < np.float32:
|
||||
if self.data.isbacked:
|
||||
raise DatasetAccessError(f"Data matrix in {self.data.X.dtype} format is not supported in backed mode."
|
||||
" Please reload without --backed, or convert matrix to float32")
|
||||
raise DatasetAccessError(
|
||||
f"Data matrix in {self.data.X.dtype} format is not supported in backed mode."
|
||||
" Please reload without --backed, or convert matrix to float32"
|
||||
)
|
||||
warnings.warn(
|
||||
f"Anndata data matrix is in unsupported {self.data.X.dtype} format -- will be cast to float32"
|
||||
)
|
||||
@@ -299,7 +302,7 @@ class AnndataAdaptor(DataAdaptor):
|
||||
layouts = self.dataset_config.embeddings__names
|
||||
|
||||
if layouts is None or len(layouts) == 0:
|
||||
layouts = [key[2:] for key in self.data.obsm_keys() if type(key) == str and key.startswith("X_")]
|
||||
layouts = [key[2:] for key in self.data.obsm_keys() if type(key) is str and key.startswith("X_")]
|
||||
|
||||
# remove invalid layouts
|
||||
valid_layouts = []
|
||||
|
||||
@@ -154,7 +154,7 @@ class DataAdaptor(metaclass=ABCMeta):
|
||||
parameters.update(self.parameters)
|
||||
|
||||
def _index_filter_to_mask(self, filter, count):
|
||||
mask = np.zeros((count,), dtype=np.bool)
|
||||
mask = np.zeros((count,), dtype="bool")
|
||||
for i in filter:
|
||||
if isinstance(i, list):
|
||||
mask[i[0] : i[1]] = True
|
||||
@@ -163,7 +163,7 @@ class DataAdaptor(metaclass=ABCMeta):
|
||||
return mask
|
||||
|
||||
def _axis_filter_to_mask(self, axis, filter, count):
|
||||
mask = np.ones((count,), dtype=np.bool)
|
||||
mask = np.ones((count,), dtype="bool")
|
||||
if "index" in filter:
|
||||
mask = np.logical_and(mask, self._index_filter_to_mask(filter["index"], count))
|
||||
if "annotation_value" in filter:
|
||||
@@ -172,7 +172,7 @@ class DataAdaptor(metaclass=ABCMeta):
|
||||
return mask
|
||||
|
||||
def _annotation_filter_to_mask(self, axis, filter, count):
|
||||
mask = np.ones((count,), dtype=np.bool)
|
||||
mask = np.ones((count,), dtype="bool")
|
||||
for v in filter:
|
||||
name = v["name"]
|
||||
if axis == Axis.VAR:
|
||||
|
||||
@@ -12,7 +12,7 @@ class MatrixDataType(Enum):
|
||||
|
||||
class MatrixDataLoader(object):
|
||||
def __init__(self, location, matrix_data_type=None, app_config=None):
|
||||
""" location can be a string or DataLocator """
|
||||
"""location can be a string or DataLocator"""
|
||||
region_name = None if app_config is None else app_config.server_config.data_locator__s3__region_name
|
||||
self.location = DataLocator(location, region_name=region_name)
|
||||
if not self.location.exists():
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
mlflow
|
||||
mlflow==1.27.0
|
||||
scanpy
|
||||
|
||||
@@ -5,6 +5,6 @@ parameterized>=0.7.0
|
||||
pytest>=3.6.3
|
||||
python-jose>=3.2.0
|
||||
twine>=1.12.1
|
||||
aiohttp>=3.9.1
|
||||
-r requirements.txt
|
||||
-r requirements-prepare.txt
|
||||
-r requirements-annotate.txt
|
||||
|
||||
+21
-24
@@ -1,25 +1,22 @@
|
||||
# NOTE: If you update 'anndata' min version, also update the 'anndata_version'
|
||||
# matrix value in .github/workflows/compatibility_tests.yml
|
||||
anndata>=0.7.6 # we need to_memory(), added in 0.7.6
|
||||
boto3>=1.12.18
|
||||
click>=7.1.2
|
||||
Flask>=1.0.2,<2.3.0
|
||||
Flask-Compress>=1.4.0
|
||||
Flask-Cors>=3.0.9 # CVE-2020-25032
|
||||
Flask-RESTful>=0.3.6
|
||||
flask-server-timing>=0.1.2
|
||||
flask-talisman>=0.7.0
|
||||
flatbuffers>=1.11.0,<2.0.0 # cellxgene is not compatible with 2.0.0. Requires migration
|
||||
flatten-dict>=0.2.0
|
||||
fsspec>=0.4.4,<0.8.0
|
||||
gunicorn>=20.0.4
|
||||
h5py>=3.0.0
|
||||
numba>=0.51.2
|
||||
numpy>=1.17.5,<=1.22
|
||||
packaging>=20.0
|
||||
pandas>=1.0,!=1.1 # pandas 1.1 breaks tests, https://github.com/pandas-dev/pandas/issues/35446
|
||||
PyYAML>=5.4 # CVE-2020-14343
|
||||
scipy>=1.4
|
||||
requests>=2.22.0
|
||||
anndata==0.10.3
|
||||
boto3==1.29.5
|
||||
click==8.1.7
|
||||
Flask==3.0.0
|
||||
Flask-Compress==1.14
|
||||
Flask-Cors==4.0.0
|
||||
Flask-RESTful==0.3.10
|
||||
flask-server-timing==0.1.2
|
||||
flask-talisman==1.1.0
|
||||
flatbuffers==1.12
|
||||
flatten-dict==0.4.2
|
||||
fsspec==2023.10.0
|
||||
gunicorn==21.2.0
|
||||
h5py==3.10.0
|
||||
numba==0.58.1
|
||||
numpy==1.26.2
|
||||
packaging==23.2
|
||||
pandas<2.0.0
|
||||
PyYAML==6.0.1
|
||||
requests==2.31.0
|
||||
s3fs==0.4.2
|
||||
# Werkzeug>=2.2.0,<3.0.0 # our version of flask doesn't support 3.0.0
|
||||
scipy==1.11.4
|
||||
Reference in New Issue
Block a user