Loading ci/recipe/meta.yaml +0 −2 Original line number Diff line number Diff line Loading @@ -21,8 +21,6 @@ requirements: - scikit-bio >=0.5.4 - numpy - blas=*=openblas # AVX 512 extensions are broken in 0.3.5 and 0.3.6 - openblas 0.3.3 - pandas - biom-format >=2.1.5,<2.2.0 - ijson Loading q2_types/_version.py +3 −3 Original line number Diff line number Diff line Loading @@ -23,9 +23,9 @@ def get_keywords(): # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = " (tag: 2019.4.1)" git_full = "39051b2475fccf77526778589b67efcbf00314cf" git_date = "2019-05-08 16:49:19 -0700" git_refnames = " (tag: 2019.7.0)" git_full = "9f31de0c81510fbe6be8b16f95e23b4c974ca002" git_date = "2019-07-30 18:15:54 +0000" keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords Loading q2_types/feature_data/__init__.py +6 −4 Original line number Diff line number Diff line Loading @@ -13,9 +13,11 @@ from ._format import ( HeaderlessTSVTaxonomyDirectoryFormat, TSVTaxonomyFormat, TSVTaxonomyDirectoryFormat, DNAFASTAFormat, DNASequencesDirectoryFormat, PairedDNASequencesDirectoryFormat, AlignedDNAFASTAFormat, AlignedDNASequencesDirectoryFormat) AlignedDNASequencesDirectoryFormat, DifferentialFormat, DifferentialDirectoryFormat) from ._type import ( FeatureData, Taxonomy, Sequence, PairedEndSequence, AlignedSequence) FeatureData, Taxonomy, Sequence, PairedEndSequence, AlignedSequence, Differential) # TODO remove these imports when tests are rewritten. Remove from __all__ too from ._transformer import DNAIterator, PairedDNAIterator, AlignedDNAIterator Loading @@ -23,11 +25,11 @@ from ._transformer import DNAIterator, PairedDNAIterator, AlignedDNAIterator __all__ = [ 'TaxonomyFormat', 'TaxonomyDirectoryFormat', 'HeaderlessTSVTaxonomyFormat', 'HeaderlessTSVTaxonomyDirectoryFormat', 'TSVTaxonomyFormat', 'TSVTaxonomyDirectoryFormat', 'DNAFASTAFormat', 'TSVTaxonomyDirectoryFormat', 'DNAFASTAFormat', 'DifferentialFormat', 'DNASequencesDirectoryFormat', 'PairedDNASequencesDirectoryFormat', 'AlignedDNAFASTAFormat', 'AlignedDNASequencesDirectoryFormat', 'FeatureData', 'Taxonomy', 'Sequence', 'PairedEndSequence', 'AlignedSequence', 'DNAIterator', 'PairedDNAIterator', 'AlignedDNAIterator'] 'AlignedDNAIterator', 'Differential', 'DifferentialDirectoryFormat'] importlib.import_module('q2_types.feature_data._transformer') q2_types/feature_data/_format.py +65 −17 Original line number Diff line number Diff line Loading @@ -6,8 +6,12 @@ # The full license is in the file LICENSE, distributed with this software. # ---------------------------------------------------------------------------- import re import skbio.io import qiime2.plugin.model as model from qiime2.plugin import ValidationError import qiime2 from ..plugin_setup import plugin Loading Loading @@ -131,23 +135,47 @@ TSVTaxonomyDirectoryFormat = model.SingleFileDirectoryFormat( class DNAFASTAFormat(model.TextFileFormat): def sniff(self): filepath = str(self) sniffer = skbio.io.io_registry.get_sniffer('fasta') if sniffer(filepath)[0]: generator = skbio.io.read(filepath, constructor=skbio.DNA, format='fasta', verify=False) def _validate_lines(self, max_lines): FASTADNAValidator = re.compile(r'[ACGTURYKMSWBDHVN]+\r?\n?') last_line_was_ID = False with open(str(self), 'rb') as fh: try: for seq, _ in zip(generator, range(5)): pass return True # ValueError raised by skbio if there are invalid DNA chars. except ValueError: pass first = fh.read(6) if first[:3] == b'\xEF\xBB\xBF': first = first[3:] # Empty files should validate if first.strip() == b'': return if first[0] != ord(b'>'): raise ValidationError("First line of file is not a valid " "FASTA ID. FASTA IDs must start " "with '>'") fh.seek(0) for line_number, line in enumerate(fh, 1): if line_number >= max_lines: return line = line.decode('utf-8-sig') if line.startswith('>'): if last_line_was_ID: raise ValidationError('Multiple consecutive IDs ' 'starting on line ' f'{line_number-1!r}') last_line_was_ID = True elif re.fullmatch(FASTADNAValidator, line): last_line_was_ID = False else: raise ValidationError('Invalid characters on line ' f'{line_number} (does not match ' 'IUPAC characters for a DNA ' 'sequence).') except UnicodeDecodeError as e: raise ValidationError(f'utf-8 cannot decode byte on line ' f'{line_number}') from e # Empty files are ok also empty_sniffer = skbio.io.io_registry.get_sniffer('<emptyfile>') return empty_sniffer(filepath)[0] def _validate_(self, max_lines): level_map = {'min': 100, 'max': float('inf')} self._validate_lines(level_map[max_lines]) DNASequencesDirectoryFormat = model.SingleFileDirectoryFormat( Loading Loading @@ -185,10 +213,30 @@ AlignedDNASequencesDirectoryFormat = model.SingleFileDirectoryFormat( AlignedDNAFASTAFormat) class DifferentialFormat(model.TextFileFormat): def validate(self, *args): try: md = qiime2.Metadata.load(str(self)) except qiime2.metadata.MetadataFileError as md_exc: raise ValidationError(md_exc) from md_exc if md.column_count == 0: raise ValidationError('Format must contain at least 1 column') filtered_md = md.filter_columns(column_type='numeric') if filtered_md.column_count != md.column_count: raise ValidationError('Must only contain numeric values.') DifferentialDirectoryFormat = model.SingleFileDirectoryFormat( 'DifferentialDirectoryFormat', 'differentials.tsv', DifferentialFormat) plugin.register_formats( TSVTaxonomyFormat, TSVTaxonomyDirectoryFormat, HeaderlessTSVTaxonomyFormat, HeaderlessTSVTaxonomyDirectoryFormat, TaxonomyFormat, TaxonomyDirectoryFormat, DNAFASTAFormat, DNASequencesDirectoryFormat, PairedDNASequencesDirectoryFormat, AlignedDNAFASTAFormat, AlignedDNASequencesDirectoryFormat AlignedDNAFASTAFormat, AlignedDNASequencesDirectoryFormat, DifferentialFormat, DifferentialDirectoryFormat ) q2_types/feature_data/_transformer.py +19 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ from ..plugin_setup import plugin from ..feature_table import BIOMV210Format from . import (TaxonomyFormat, HeaderlessTSVTaxonomyFormat, TSVTaxonomyFormat, DNAFASTAFormat, PairedDNASequencesDirectoryFormat, AlignedDNAFASTAFormat) AlignedDNAFASTAFormat, DifferentialFormat) # Taxonomy format transformers Loading Loading @@ -359,3 +359,21 @@ def _19(data: AlignedDNAIterator) -> AlignedDNAFASTAFormat: @plugin.register_transformer def _33(ff: AlignedDNAFASTAFormat) -> qiime2.Metadata: return _dnafastaformats_to_metadata(ff) # differential types @plugin.register_transformer def _222(ff: DifferentialFormat) -> pd.DataFrame: return qiime2.Metadata.load(str(ff)).to_dataframe() @plugin.register_transformer def _223(ff: DifferentialFormat) -> qiime2.Metadata: return qiime2.Metadata.load(str(ff)) @plugin.register_transformer def _224(data: pd.DataFrame) -> DifferentialFormat: ff = DifferentialFormat() qiime2.Metadata(data).save(str(ff)) return ff Loading
ci/recipe/meta.yaml +0 −2 Original line number Diff line number Diff line Loading @@ -21,8 +21,6 @@ requirements: - scikit-bio >=0.5.4 - numpy - blas=*=openblas # AVX 512 extensions are broken in 0.3.5 and 0.3.6 - openblas 0.3.3 - pandas - biom-format >=2.1.5,<2.2.0 - ijson Loading
q2_types/_version.py +3 −3 Original line number Diff line number Diff line Loading @@ -23,9 +23,9 @@ def get_keywords(): # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = " (tag: 2019.4.1)" git_full = "39051b2475fccf77526778589b67efcbf00314cf" git_date = "2019-05-08 16:49:19 -0700" git_refnames = " (tag: 2019.7.0)" git_full = "9f31de0c81510fbe6be8b16f95e23b4c974ca002" git_date = "2019-07-30 18:15:54 +0000" keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords Loading
q2_types/feature_data/__init__.py +6 −4 Original line number Diff line number Diff line Loading @@ -13,9 +13,11 @@ from ._format import ( HeaderlessTSVTaxonomyDirectoryFormat, TSVTaxonomyFormat, TSVTaxonomyDirectoryFormat, DNAFASTAFormat, DNASequencesDirectoryFormat, PairedDNASequencesDirectoryFormat, AlignedDNAFASTAFormat, AlignedDNASequencesDirectoryFormat) AlignedDNASequencesDirectoryFormat, DifferentialFormat, DifferentialDirectoryFormat) from ._type import ( FeatureData, Taxonomy, Sequence, PairedEndSequence, AlignedSequence) FeatureData, Taxonomy, Sequence, PairedEndSequence, AlignedSequence, Differential) # TODO remove these imports when tests are rewritten. Remove from __all__ too from ._transformer import DNAIterator, PairedDNAIterator, AlignedDNAIterator Loading @@ -23,11 +25,11 @@ from ._transformer import DNAIterator, PairedDNAIterator, AlignedDNAIterator __all__ = [ 'TaxonomyFormat', 'TaxonomyDirectoryFormat', 'HeaderlessTSVTaxonomyFormat', 'HeaderlessTSVTaxonomyDirectoryFormat', 'TSVTaxonomyFormat', 'TSVTaxonomyDirectoryFormat', 'DNAFASTAFormat', 'TSVTaxonomyDirectoryFormat', 'DNAFASTAFormat', 'DifferentialFormat', 'DNASequencesDirectoryFormat', 'PairedDNASequencesDirectoryFormat', 'AlignedDNAFASTAFormat', 'AlignedDNASequencesDirectoryFormat', 'FeatureData', 'Taxonomy', 'Sequence', 'PairedEndSequence', 'AlignedSequence', 'DNAIterator', 'PairedDNAIterator', 'AlignedDNAIterator'] 'AlignedDNAIterator', 'Differential', 'DifferentialDirectoryFormat'] importlib.import_module('q2_types.feature_data._transformer')
q2_types/feature_data/_format.py +65 −17 Original line number Diff line number Diff line Loading @@ -6,8 +6,12 @@ # The full license is in the file LICENSE, distributed with this software. # ---------------------------------------------------------------------------- import re import skbio.io import qiime2.plugin.model as model from qiime2.plugin import ValidationError import qiime2 from ..plugin_setup import plugin Loading Loading @@ -131,23 +135,47 @@ TSVTaxonomyDirectoryFormat = model.SingleFileDirectoryFormat( class DNAFASTAFormat(model.TextFileFormat): def sniff(self): filepath = str(self) sniffer = skbio.io.io_registry.get_sniffer('fasta') if sniffer(filepath)[0]: generator = skbio.io.read(filepath, constructor=skbio.DNA, format='fasta', verify=False) def _validate_lines(self, max_lines): FASTADNAValidator = re.compile(r'[ACGTURYKMSWBDHVN]+\r?\n?') last_line_was_ID = False with open(str(self), 'rb') as fh: try: for seq, _ in zip(generator, range(5)): pass return True # ValueError raised by skbio if there are invalid DNA chars. except ValueError: pass first = fh.read(6) if first[:3] == b'\xEF\xBB\xBF': first = first[3:] # Empty files should validate if first.strip() == b'': return if first[0] != ord(b'>'): raise ValidationError("First line of file is not a valid " "FASTA ID. FASTA IDs must start " "with '>'") fh.seek(0) for line_number, line in enumerate(fh, 1): if line_number >= max_lines: return line = line.decode('utf-8-sig') if line.startswith('>'): if last_line_was_ID: raise ValidationError('Multiple consecutive IDs ' 'starting on line ' f'{line_number-1!r}') last_line_was_ID = True elif re.fullmatch(FASTADNAValidator, line): last_line_was_ID = False else: raise ValidationError('Invalid characters on line ' f'{line_number} (does not match ' 'IUPAC characters for a DNA ' 'sequence).') except UnicodeDecodeError as e: raise ValidationError(f'utf-8 cannot decode byte on line ' f'{line_number}') from e # Empty files are ok also empty_sniffer = skbio.io.io_registry.get_sniffer('<emptyfile>') return empty_sniffer(filepath)[0] def _validate_(self, max_lines): level_map = {'min': 100, 'max': float('inf')} self._validate_lines(level_map[max_lines]) DNASequencesDirectoryFormat = model.SingleFileDirectoryFormat( Loading Loading @@ -185,10 +213,30 @@ AlignedDNASequencesDirectoryFormat = model.SingleFileDirectoryFormat( AlignedDNAFASTAFormat) class DifferentialFormat(model.TextFileFormat): def validate(self, *args): try: md = qiime2.Metadata.load(str(self)) except qiime2.metadata.MetadataFileError as md_exc: raise ValidationError(md_exc) from md_exc if md.column_count == 0: raise ValidationError('Format must contain at least 1 column') filtered_md = md.filter_columns(column_type='numeric') if filtered_md.column_count != md.column_count: raise ValidationError('Must only contain numeric values.') DifferentialDirectoryFormat = model.SingleFileDirectoryFormat( 'DifferentialDirectoryFormat', 'differentials.tsv', DifferentialFormat) plugin.register_formats( TSVTaxonomyFormat, TSVTaxonomyDirectoryFormat, HeaderlessTSVTaxonomyFormat, HeaderlessTSVTaxonomyDirectoryFormat, TaxonomyFormat, TaxonomyDirectoryFormat, DNAFASTAFormat, DNASequencesDirectoryFormat, PairedDNASequencesDirectoryFormat, AlignedDNAFASTAFormat, AlignedDNASequencesDirectoryFormat AlignedDNAFASTAFormat, AlignedDNASequencesDirectoryFormat, DifferentialFormat, DifferentialDirectoryFormat )
q2_types/feature_data/_transformer.py +19 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ from ..plugin_setup import plugin from ..feature_table import BIOMV210Format from . import (TaxonomyFormat, HeaderlessTSVTaxonomyFormat, TSVTaxonomyFormat, DNAFASTAFormat, PairedDNASequencesDirectoryFormat, AlignedDNAFASTAFormat) AlignedDNAFASTAFormat, DifferentialFormat) # Taxonomy format transformers Loading Loading @@ -359,3 +359,21 @@ def _19(data: AlignedDNAIterator) -> AlignedDNAFASTAFormat: @plugin.register_transformer def _33(ff: AlignedDNAFASTAFormat) -> qiime2.Metadata: return _dnafastaformats_to_metadata(ff) # differential types @plugin.register_transformer def _222(ff: DifferentialFormat) -> pd.DataFrame: return qiime2.Metadata.load(str(ff)).to_dataframe() @plugin.register_transformer def _223(ff: DifferentialFormat) -> qiime2.Metadata: return qiime2.Metadata.load(str(ff)) @plugin.register_transformer def _224(data: pd.DataFrame) -> DifferentialFormat: ff = DifferentialFormat() qiime2.Metadata(data).save(str(ff)) return ff