smftools 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. smftools/__init__.py +27 -0
  2. smftools/_settings.py +19 -0
  3. smftools/datasets/F1_hybrid_NKG2A_enhander_promoter_GpC_conversion_SMF.h5ad.gz +0 -0
  4. smftools/datasets/__init__.py +9 -0
  5. smftools/datasets/dCas9_m6A_invitro_kinetics.h5ad.gz +0 -0
  6. smftools/datasets/datasets.py +25 -0
  7. smftools/informatics/__init__.py +11 -0
  8. smftools/informatics/helpers/__init__.py +42 -0
  9. smftools/informatics/helpers/align_BAM.py +49 -0
  10. smftools/informatics/helpers/binarize_converted_base_identities.py +24 -0
  11. smftools/informatics/helpers/canoncall.py +12 -0
  12. smftools/informatics/helpers/converted_BAM_to_adata.py +147 -0
  13. smftools/informatics/helpers/count_aligned_reads.py +32 -0
  14. smftools/informatics/helpers/extract_base_identities.py +36 -0
  15. smftools/informatics/helpers/extract_mods.py +39 -0
  16. smftools/informatics/helpers/find_conversion_sites.py +53 -0
  17. smftools/informatics/helpers/generate_converted_FASTA.py +59 -0
  18. smftools/informatics/helpers/get_native_references.py +25 -0
  19. smftools/informatics/helpers/informatics.py +260 -0
  20. smftools/informatics/helpers/load_adata.py +516 -0
  21. smftools/informatics/helpers/load_experiment_config.py +17 -0
  22. smftools/informatics/helpers/make_dirs.py +15 -0
  23. smftools/informatics/helpers/make_modbed.py +21 -0
  24. smftools/informatics/helpers/modQC.py +19 -0
  25. smftools/informatics/helpers/modcall.py +14 -0
  26. smftools/informatics/helpers/modkit_extract_to_adata.py +355 -0
  27. smftools/informatics/helpers/one_hot_encode.py +14 -0
  28. smftools/informatics/helpers/separate_bam_by_bc.py +28 -0
  29. smftools/informatics/helpers/split_and_index_BAM.py +21 -0
  30. smftools/informatics/pod5_conversion.py +26 -0
  31. smftools/informatics/pod5_direct.py +29 -0
  32. smftools/informatics/pod5_to_adata.py +17 -0
  33. smftools/informatics/readwrite.py +109 -0
  34. smftools/plotting/__init__.py +0 -0
  35. smftools/preprocessing/__init__.py +35 -0
  36. smftools/preprocessing/append_C_context.py +39 -0
  37. smftools/preprocessing/binarize_on_Youden.py +38 -0
  38. smftools/preprocessing/binary_layers_to_ohe.py +25 -0
  39. smftools/preprocessing/calculate_complexity.py +59 -0
  40. smftools/preprocessing/calculate_converted_read_methylation_stats.py +38 -0
  41. smftools/preprocessing/calculate_coverage.py +35 -0
  42. smftools/preprocessing/calculate_pairwise_hamming_distances.py +22 -0
  43. smftools/preprocessing/calculate_position_Youden.py +95 -0
  44. smftools/preprocessing/calculate_read_length_stats.py +27 -0
  45. smftools/preprocessing/clean_NaN.py +31 -0
  46. smftools/preprocessing/filter_converted_reads_on_methylation.py +20 -0
  47. smftools/preprocessing/filter_reads_on_length.py +31 -0
  48. smftools/preprocessing/invert_adata.py +18 -0
  49. smftools/preprocessing/mark_duplicates.py +110 -0
  50. smftools/preprocessing/min_non_diagonal.py +20 -0
  51. smftools/preprocessing/preprocessing.py +614 -0
  52. smftools/preprocessing/remove_duplicates.py +12 -0
  53. smftools/readwrite.py +109 -0
  54. smftools/tools/__init__.py +0 -0
  55. smftools-0.1.0.dist-info/METADATA +75 -0
  56. smftools-0.1.0.dist-info/RECORD +58 -0
  57. smftools-0.1.0.dist-info/WHEEL +4 -0
  58. smftools-0.1.0.dist-info/licenses/LICENSE +21 -0
smftools/readwrite.py ADDED
@@ -0,0 +1,109 @@
1
+ ## readwrite ##
2
+
3
+ # Basic I/O
4
+ import os
5
+ # Datetime
6
+ from datetime import datetime
7
+ # Data structures and basic operations
8
+ import math
9
+ import numpy as np
10
+ import pandas as pd
11
+ import anndata as ad
12
+ import scipy.sparse as sp
13
+
14
+ # Runtime warnings
15
+ import warnings
16
+ warnings.filterwarnings('ignore', category=UserWarning, module='anndata')
17
+ warnings.filterwarnings('ignore', category=FutureWarning, module='anndata')
18
+
19
+ ######################################################################################################
20
+ ## Datetime functionality
21
+ def date_string():
22
+ """
23
+ Each time this is called, it returns the current date string
24
+ """
25
+ current_date = datetime.now()
26
+ date_string = current_date.strftime("%Y%m%d")
27
+ date_string = date_string[2:]
28
+ return date_string
29
+
30
+ def time_string():
31
+ """
32
+ Each time this is called, it returns the current time string
33
+ """
34
+ current_time = datetime.now()
35
+ return current_time.strftime("%H:%M:%S")
36
+ ######################################################################################################
37
+
38
+ ######################################################################################################
39
+ ## Numpy, Pandas, Anndata functionality
40
+ def adata_to_df(adata, layer=None):
41
+ """
42
+ Input: An adata object with a specified layer.
43
+ Output: A dataframe for the specific layer.
44
+ """
45
+ # Extract the data matrix from the given layer
46
+ if layer:
47
+ data_matrix = adata.layers[layer]
48
+ else:
49
+ data_matrix = adata.X
50
+ # Extract observation (read) annotations
51
+ obs_df = adata.obs
52
+ # Extract variable (position) annotations
53
+ var_df = adata.var
54
+ # Convert data matrix and annotations to pandas DataFrames
55
+ df = pd.DataFrame(data_matrix, index=obs_df.index, columns=var_df.index)
56
+ return df
57
+
58
+ def save_matrix(matrix, save_name):
59
+ """
60
+ Input: A numpy matrix and a save_name
61
+ Output: A txt file representation of the data matrix
62
+ """
63
+ np.savetxt(f'{save_name}.txt', matrix)
64
+
65
+ def concatenate_h5ads(output_file, file_suffix='h5ad.gz', delete_inputs=True):
66
+ """
67
+ Concatenate all h5ad files in a directory and delete them after the final adata is written out.
68
+ Input: an output file path relative to the directory in which the function is called
69
+ """
70
+ # List all files in the directory
71
+ files = os.listdir(os.getcwd())
72
+ # get current working directory
73
+ cwd = os.getcwd()
74
+ suffix = file_suffix
75
+ # Filter file names that contain the search string in their filename and keep them in a list
76
+ hdfs = [hdf for hdf in files if suffix in hdf]
77
+ # Sort file list by names and print the list of file names
78
+ hdfs.sort()
79
+ print('{0} sample files found: {1}'.format(len(hdfs), hdfs))
80
+ # Iterate over all of the hdf5 files and concatenate them.
81
+ final_adata = None
82
+ for hdf in hdfs:
83
+ print('{0}: Reading in {1} hdf5 file'.format(time_string(), hdf))
84
+ temp_adata = ad.read_h5ad(hdf)
85
+ if final_adata:
86
+ print('{0}: Concatenating final adata object with {1} hdf5 file'.format(time_string(), hdf))
87
+ final_adata = ad.concat([final_adata, temp_adata], join='outer', index_unique=None)
88
+ else:
89
+ print('{0}: Initializing final adata object with {1} hdf5 file'.format(time_string(), hdf))
90
+ final_adata = temp_adata
91
+ print('{0}: Writing final concatenated hdf5 file'.format(time_string()))
92
+ final_adata.write_h5ad(output_file, compression='gzip')
93
+
94
+ # Delete the individual h5ad files and only keep the final concatenated file
95
+ if delete_inputs:
96
+ files = os.listdir(os.getcwd())
97
+ hdfs = [hdf for hdf in files if suffix in hdf]
98
+ if output_file in hdfs:
99
+ hdfs.remove(output_file)
100
+ # Iterate over the files and delete them
101
+ for hdf in hdfs:
102
+ try:
103
+ os.remove(hdf)
104
+ print(f"Deleted file: {hdf}")
105
+ except OSError as e:
106
+ print(f"Error deleting file {hdf}: {e}")
107
+ else:
108
+ print('Keeping input files')
109
+ ######################################################################################################
File without changes
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.3
2
+ Name: smftools
3
+ Version: 0.1.0
4
+ Summary: Single Molecule Footprinting Analysis in Python.
5
+ Project-URL: Source, https://github.com/jkmckenna/smftools
6
+ Author: Joseph McKenna
7
+ Maintainer-email: Joseph McKenna <jkmckenna@berkeley.edu>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: anndata,chromatin-accessibility,machine-learning,nanopore,protein-dna-binding,single-locus,single-molecule-footprinting
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: MacOS :: MacOS X
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
24
+ Classifier: Topic :: Scientific/Engineering :: Visualization
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: anndata>=0.10.0
27
+ Requires-Dist: biopython>=1.79
28
+ Requires-Dist: cython>=0.29.28
29
+ Requires-Dist: networkx>=3.2
30
+ Requires-Dist: numpy<2,>=1.22.0
31
+ Requires-Dist: pandas>=1.4.2
32
+ Requires-Dist: pomegranate>1.0.0
33
+ Requires-Dist: pysam>=0.19.1
34
+ Requires-Dist: scanpy>=1.9
35
+ Requires-Dist: scikit-learn>=1.0.2
36
+ Requires-Dist: scipy>=1.7.3
37
+ Requires-Dist: seaborn>=0.11
38
+ Requires-Dist: tqdm
39
+ Provides-Extra: base-tests
40
+ Requires-Dist: pytest; extra == 'base-tests'
41
+ Requires-Dist: pytest-cov; extra == 'base-tests'
42
+ Provides-Extra: doc
43
+ Requires-Dist: ipython>=7.20; extra == 'doc'
44
+ Requires-Dist: matplotlib!=3.6.1; extra == 'doc'
45
+ Requires-Dist: myst-nb>=1; extra == 'doc'
46
+ Requires-Dist: myst-parser>=2; extra == 'doc'
47
+ Requires-Dist: nbsphinx>=0.9; extra == 'doc'
48
+ Requires-Dist: readthedocs-sphinx-search; extra == 'doc'
49
+ Requires-Dist: setuptools; extra == 'doc'
50
+ Requires-Dist: sphinx-autodoc-typehints>=1.25.2; extra == 'doc'
51
+ Requires-Dist: sphinx-book-theme>=1.1.0; extra == 'doc'
52
+ Requires-Dist: sphinx-copybutton; extra == 'doc'
53
+ Requires-Dist: sphinx-design; extra == 'doc'
54
+ Requires-Dist: sphinx>=7; extra == 'doc'
55
+ Requires-Dist: sphinxcontrib-bibtex; extra == 'doc'
56
+ Requires-Dist: sphinxext-opengraph; extra == 'doc'
57
+ Provides-Extra: torch
58
+ Requires-Dist: pomeganate>=1.0.0; extra == 'torch'
59
+ Requires-Dist: torch>=1.9.0; extra == 'torch'
60
+ Provides-Extra: torch-tests
61
+ Requires-Dist: pomeganate>=1.0.0; extra == 'torch-tests'
62
+ Requires-Dist: pytest; extra == 'torch-tests'
63
+ Requires-Dist: pytest-cov; extra == 'torch-tests'
64
+ Requires-Dist: torch>=1.9.0; extra == 'torch-tests'
65
+ Description-Content-Type: text/markdown
66
+
67
+ # smftools
68
+ A tool for processing raw sequencing data for single molecule footprinting experiments at single genomic loci.
69
+
70
+ ## Dependencies
71
+ The following tools need to be installed and configured:
72
+ 1) [Dorado](https://github.com/nanoporetech/dorado) -> For standard/modified basecalling and alignment. Can be attained by downloading and configuring nanopore MinKnow software.
73
+ 2) [Samtools](https://github.com/samtools/samtools) -> For working with SAM/BAM files
74
+ 3) [Minimap2](https://github.com/lh3/minimap2) -> The aligner used by Dorado
75
+ 4) [Modkit](https://github.com/nanoporetech/modkit) -> Extracting summary statistics and read level methylation calls from modified BAM files
@@ -0,0 +1,58 @@
1
+ smftools/__init__.py,sha256=pWcysCXCokCdW4YySaA8BMumZkE56m15otMPG88nQGc,444
2
+ smftools/_settings.py,sha256=a1uYWNBNtQb30cGSdpjeiIMnQV1Fip7IZAQrNzjXR5w,324
3
+ smftools/readwrite.py,sha256=p-K_RYOrM0vDawBTcuCUyuwVzmYwJqNMvhv9fCTLDKE,4159
4
+ smftools/datasets/F1_hybrid_NKG2A_enhander_promoter_GpC_conversion_SMF.h5ad.gz,sha256=q6wJtgFRDln0o20XNCx1qad3lwcdCoylqPN7wskTfI8,2926497
5
+ smftools/datasets/__init__.py,sha256=xkSTlPuakVYVCuRurif9BceNBDt6bsngJvvjI8757QI,142
6
+ smftools/datasets/dCas9_m6A_invitro_kinetics.h5ad.gz,sha256=niOcVHaYY7h3XyvwSkN-V_NMBaRt2vTP5TrJO0CwMCs,8385050
7
+ smftools/datasets/datasets.py,sha256=rAcp7_Raa8Uv95DISj-oACY1fE_5fIfb5Poj-9WVOWo,473
8
+ smftools/informatics/__init__.py,sha256=Bjufdncl978d-tNriuRHX92mjeAO5axjTlZP7iePjms,235
9
+ smftools/informatics/pod5_conversion.py,sha256=m_qNRSNeUndl5KO8PJPMLCOWqcVq-TJSvoyUnJW-UHE,1399
10
+ smftools/informatics/pod5_direct.py,sha256=MGQkpHI2qQuEO0IDFOiXjL0Pq59oC1DwUaIWH7jTHiU,1707
11
+ smftools/informatics/pod5_to_adata.py,sha256=R31bkGbparRGpYpTZ69znQThK1xCGChlf4oP836IC9Y,722
12
+ smftools/informatics/readwrite.py,sha256=p-K_RYOrM0vDawBTcuCUyuwVzmYwJqNMvhv9fCTLDKE,4159
13
+ smftools/informatics/helpers/__init__.py,sha256=ws8Zyxin68L7G5R9Rna_qoBnkSNOaD1ndlcrooV2d-k,1466
14
+ smftools/informatics/helpers/align_BAM.py,sha256=vZpkbI-mUqd6qJaovRhuNM03s816fjd5hNEcfA0oHxo,1414
15
+ smftools/informatics/helpers/binarize_converted_base_identities.py,sha256=rTdk06BmU_bvuE1NOU1LGSQs9ytkl7vQjZcwMbA5Yx0,1409
16
+ smftools/informatics/helpers/canoncall.py,sha256=Ujz0Pkp_wW-XJyb3uB2fzVpB12c2MCOWVTg_uIOQL8c,397
17
+ smftools/informatics/helpers/converted_BAM_to_adata.py,sha256=7tntnkTZpNqS0WTMbO42ksxXqzk9NFpXK0q6uWSqtkM,8593
18
+ smftools/informatics/helpers/count_aligned_reads.py,sha256=Q9iU0zwwNZRn0oOxRl_x5OAF7YgaRkSpQBojaBccQsI,1814
19
+ smftools/informatics/helpers/extract_base_identities.py,sha256=nrQy8cUyOA2C8cKKL6SpZ97U0ZGhChw8Qdk0BwuxIT4,2406
20
+ smftools/informatics/helpers/extract_mods.py,sha256=IQdpQxh_2NStKK4kIVKa8UAcV-fVJSs_bzB3JLe0Jx4,1602
21
+ smftools/informatics/helpers/find_conversion_sites.py,sha256=aer63p2JHqaoB3wSK9xqpSjow7w7UyrMSJ06aKTQSiQ,3208
22
+ smftools/informatics/helpers/generate_converted_FASTA.py,sha256=xHjspkeCiSKjqb6zUrjCG13OKW597LJ4_w33tg-wFok,3006
23
+ smftools/informatics/helpers/get_native_references.py,sha256=wx_RXnPwj0NGp7Tx1_hXyO8ZzQJHZwa0b3a6r3266FY,976
24
+ smftools/informatics/helpers/informatics.py,sha256=gKb2ZJ_LcAeEXuQqn9e-QDF_sS4tMpMTr2vZlqa7n54,14572
25
+ smftools/informatics/helpers/load_adata.py,sha256=DhvYYqO9VLsZqhL1WjN9sd-e3fgvdXGlgTP18z1h0L0,33654
26
+ smftools/informatics/helpers/load_experiment_config.py,sha256=boF524jZZKzBjc2yvAiMYvhM4OW_efXSbwU-nINKDdg,607
27
+ smftools/informatics/helpers/make_dirs.py,sha256=UxghbuquyXgDD-H24Ghf1B7Kfpdt04NgMs8GE6zSJ3U,475
28
+ smftools/informatics/helpers/make_modbed.py,sha256=m1lQUbw0W63YjiM_Tmy6QOL3GkHgvSuCrkX2Bo8sCco,741
29
+ smftools/informatics/helpers/modQC.py,sha256=C-WVaoLN7Dxh--JcWa4UXzhwFpf0AXrFA99IsxfVXwo,770
30
+ smftools/informatics/helpers/modcall.py,sha256=f41SkaXi2fYgv7B0oAFwj-9CZ0XVhvzWHfe977rT0wQ,493
31
+ smftools/informatics/helpers/modkit_extract_to_adata.py,sha256=TvIHDXWTO2QxArGyIU3w-dQBVyq0rQ1A4iMvV9Rb_7A,24407
32
+ smftools/informatics/helpers/one_hot_encode.py,sha256=jxfTNREED0YhdvwhVmRrt2BZUfiOrOUURVSOVtGypns,439
33
+ smftools/informatics/helpers/separate_bam_by_bc.py,sha256=AFT-v0XXuW2rRYG6FC-8gulhAA6a4YGAa0UEuMDlimc,1235
34
+ smftools/informatics/helpers/split_and_index_BAM.py,sha256=HqPAy5YxK0jokCWCUlUIWSLf2n-Gubkge-xxYfX4XLE,755
35
+ smftools/plotting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ smftools/preprocessing/__init__.py,sha256=ngs4XYcd9gSXwSa-gi1pWMblCRJ_uZo5MMkg5vuhDOU,1438
37
+ smftools/preprocessing/append_C_context.py,sha256=sfHiV3gBOum3KyZ6wFOCUcDUieW9rra3hKFkWEb8wmk,2063
38
+ smftools/preprocessing/binarize_on_Youden.py,sha256=CO0KnxxHmCwq7tlrBh7BuY0_6SEacR7NwRhkwaWoVUg,2056
39
+ smftools/preprocessing/binary_layers_to_ohe.py,sha256=zahq1YcgAeva-b1CcEVaBK4XaNmMgE6IpfiedYepI5k,876
40
+ smftools/preprocessing/calculate_complexity.py,sha256=H4j0mCWL-jFWZ8UoTGa5lNEcqByfwIDtlsJrpNRNkg4,2751
41
+ smftools/preprocessing/calculate_converted_read_methylation_stats.py,sha256=ZIOKGkbWI15RzpnfgWU4MUXFz3LlUjL_yjodGrye8-A,2626
42
+ smftools/preprocessing/calculate_coverage.py,sha256=Q-RjTqbYt9jc-Axk807_h0m7_oDFdewrO805FQARLUA,1852
43
+ smftools/preprocessing/calculate_pairwise_hamming_distances.py,sha256=l0IYlWu9RCDq4R2pJm_qGXN_RyFkIuah9fFLvg1Hti0,843
44
+ smftools/preprocessing/calculate_position_Youden.py,sha256=FUSsrhp8L8TLoJaX2cSl8u1phNbYpTRJfVqsrwMOWgY,6008
45
+ smftools/preprocessing/calculate_read_length_stats.py,sha256=kKcEw4zS-GnJ2nyC5c24YVMY2oBmxmcxjPWLGnrkwws,1711
46
+ smftools/preprocessing/clean_NaN.py,sha256=HCRX_nA6H3o7CysCa6yxN07xQEoh6LvdkX7aAYqSKR8,1024
47
+ smftools/preprocessing/filter_converted_reads_on_methylation.py,sha256=krqDb6TNjQx4IICXbEQ8SDcaSjrWZ-9ChtaEiIxU5KY,962
48
+ smftools/preprocessing/filter_reads_on_length.py,sha256=-tXMIpg8Mx8GskCfjBy0ZBczuJRTZdyuSZtDyb6KDJs,1737
49
+ smftools/preprocessing/invert_adata.py,sha256=vpR0jynLODhE8mpiHZQIv1XUY9pd7cEG0ujC-GArXIE,616
50
+ smftools/preprocessing/mark_duplicates.py,sha256=Qd1fluCHkL7ZAY37wGmBe40HwkRipOkbDAp6lnoLU9I,6818
51
+ smftools/preprocessing/min_non_diagonal.py,sha256=o79E5xy-aO-cSwN5dUVi5oj8_EfQBDPcj1D0_7fvk1Q,644
52
+ smftools/preprocessing/preprocessing.py,sha256=4mLT09A7vwRZ78FHmuwtv38mH9TQ9qrZc_WjHRhhkIw,34379
53
+ smftools/preprocessing/remove_duplicates.py,sha256=sgdRjZSLakocTRwAukdp1RpFhODbeOjNN_EWZkTshAc,395
54
+ smftools/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
+ smftools-0.1.0.dist-info/METADATA,sha256=Loh3iFQgPLn6Xe_WdbeATlGiMqggFaYJOqgwf5e8WRI,3422
56
+ smftools-0.1.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
57
+ smftools-0.1.0.dist-info/licenses/LICENSE,sha256=F8LwmL6vMPddaCt1z1S83Kh_OZv50alTlY7BvVx1RXw,1066
58
+ smftools-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.25.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 jkmckenna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.