docs: def validate_conventions(self, ds: loompy.LoomConnection) -> bool:
  		"""
  		Validate the LoomConnection object against the attribute name/dtype conventions.
  
  		Args:
  			ds:			LoomConnection object
  		
  		Returns:
  			True if the file conforms to the conventions, else False
  		
  		Remarks:
  			Upon return, the instance attributes 'self.errors' and 'self.warnings' contain
  			lists of errors and warnings.
  		"""
  		(n_genes, n_cells) = ds.shape
  
  		self._warn("Description" in ds.attrs, "Optional global attribute 'Description' is missing")
  		self._warn("Journal" in ds.attrs, "Optional global attribute 'Journal' is missing")
  		self._warn("Authors" in ds.attrs, "Optional global attribute 'Authors' is missing")
  		self._warn("Title" in ds.attrs, "Optional global attribute 'Title' is missing")
  		self._warn("Year" in ds.attrs, "Optional global attribute 'Year' is missing")
  		self._warn("CreationDate" in ds.attrs, "Optional global attribute 'CreationDate' is missing")
  
  		if self._check("ClusterID" in ds.ca, "Column attribute 'ClusterID' is missing"):
  			self._check(np.issubdtype(ds.ca.ClusterID.dtype, np.int_), "Column attribute 'ClusterID' must be integer dtype")
  			self._check(len(np.unique(ds.ca.ClusterID)) == np.max(ds.ca.ClusterID) and np.min(ds.ca.ClusterID) == 0, "Column attribute 'ClusterID' must be integers 0, 1, 2, ... with no missing values")
  			self._check(ds.ca.ClusterID.shape == (n_cells,), f"Column attribute 'ClusterID' must be 1-dimensional array of {n_cells} elements")
  
  		if "ClusterName" in ds.ca:
  			self._check(ds.ca.ClusterName.dtype == object and np.issubdtype(ds.ca.ClusterName[0].dtype, np.str_), "Column attribute 'ClusterName' must be an array of strings")
  			self._check(ds.ca.ClusterName.shape == (n_cells,), f"Column attribute 'ClusterName' must be 1-dimensional array of {n_cells} elements")
  			one_to_one = True
  			for cid in np.unique(ds.ca.ClusterID):
  				if len(np.unique(ds.ca.ClusterName[ds.ca.ClusterID == cid])) != 1:
  					one_to_one = False
  					break
  			for cn in np.unique(ds.ca.ClusterName):
  				if len(np.unique(ds.ca.ClusterID[ds.ca.ClusterName == cn])) != 1:
  					one_to_one = False
  					break
  			if not one_to_one:
  				self._check(False, "ClusterName must correspond 1:1 with ClusterID")
  		else:
  			self.warnings.append("Optional column attribute 'ClusterName' is missing")
  
  		if self._check("CellID" in ds.ca, "Column attribute 'CellID' is missing"):
  			self._check(ds.ca.CellID.dtype == object and np.issubdtype(ds.ca.CellID[0].dtype, np.str_), f"Column attribute 'CellID' must be an array of strings, not '{ds.ca.CellID[0].dtype}'")
  			self._check(ds.ca.CellID.shape == (n_cells,), f"Column attribute 'CellID' must be 1-dimensional array of {n_cells} elements")
  			self._check(len(np.unique(ds.ca.CellID)) == n_cells, "Column attribute 'CellID' cannot contain duplicate values")
  
  		if "Valid" in ds.ca:
  			self._check(np.issubdtype(ds.ca.Valid.dtype, np.int_), f"Column attribute 'Valid' must be integer dtype, not '{ds.ca.Valid.dtype}'")
  			valids = np.unique(ds.ca.Valid)
  			self._check(np.all(np.isin(ds.ca.Valid, [0, 1])), "Column attribute 'Valid' must be integers 0 or 1 only")
  			self._check(ds.ca.Valid.shape == (n_cells,), f"Column attribute 'Valid' must be 1-dimensional array of {n_cells} elements")
  		else:
  			self.warnings.append("Optional column attribute 'Valid' is missing")
  
  		if "Outliers" in ds.ca:
  			self._check(np.issubdtype(ds.ca.Outliers.dtype, np.int_), f"Column attribute 'Outliers' must be integer dtype, not '{ds.ca.Outliers.dtype}'")
  			self._check(np.all(np.isin(ds.ca.Outliers, [0, 1])), "Column attribute 'Outliers' must be integers 0 or 1 only")
  			self._check(ds.ca.Outliers.shape == (n_cells,), f"Column attribute 'Outliers' must be 1-dimensional array of {n_cells} elements")
  		else:
  			self.warnings.append("Optional column attribute 'Outliers' is missing")
  
  		if self._check("Accession" in ds.ra, "Row attribute 'Accession' is missing"):
  			self._check(ds.ra.Accession.dtype == object and np.issubdtype(ds.ra.Accession[0].dtype, np.str_), f"Row attribute 'Accession' must be an array of strings, not '{ds.ra.Accession[0].dtype}'")
  			self._check(ds.ra.Accession.shape == (n_genes,), f"Row attribute 'Accession' must be 1-dimensional array of {n_genes} elements")
  			self._check(len(np.unique(ds.ra.Accession)) == n_genes, "Row attribute 'Accession' cannot contain duplicate values")
  
  		if self._check("Gene" in ds.ra, "Row attribute 'Gene' is missing"):
  			self._check(ds.ra.Gene.dtype == object and np.issubdtype(ds.ra.Gene[0].dtype, np.str_), f"Row attribute 'Gene' must be an array of strings, not '{ds.ra.Gene[0].dtype}'")
  			self._check(ds.ra.Gene.shape == (n_genes,), f"Row attribute 'Gene' must be 1-dimensional array of {n_genes} elements")
  
  		if "Valid" in ds.ra:
  			self._check(np.issubdtype(ds.ra.Valid.dtype, np.int_), f"Row attribute 'Valid' must be integer dtype, not '{ds.ra.Valid.dtype}'")
  			valids = np.unique(ds.ra.Valid)
  			self._check(np.all(np.isin(ds.ra.Valid, [0, 1])), "Row attribute 'Valid' must be integers 0 or 1 only")
  			self._check(ds.ra.Valid.shape == (n_cells,), f"Row attribute 'Valid' must be 1-dimensional array of {n_cells} elements")
  		else:
  			self.warnings.append("Optional row attribute 'Valid' is missing")
  
  		if "Selected" in ds.ra:
  			self._check(np.issubdtype(ds.ra.Selected.dtype, np.int_), f"Row attribute 'Selected' must be integer dtype, not '{ds.ra.Selected.dtype}'")
  			valids = np.unique(ds.ra.Selected)
  			self._check(np.all(np.isin(ds.ra.Selected, [0, 1])), "Row attribute 'Selected' must be integers 0 or 1 only")
  			self._check(ds.ra.Selected.shape == (n_cells,), f"Row attribute 'Selected' must be 1-dimensional array of {n_cells} elements")
  		else:
  			self.warnings.append("Optional row attribute 'Selected' is missing")
  
  		return len(self.errors) == 0