podns 0.1.1__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.
podns/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ """
2
+ MIT License
3
+
4
+ Copyright (c) 2024-present abigail phoebe <abigail@phoebe.sh>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ """
24
+
25
+ from .dns import fetch_pronouns_from_domain_sync, fetch_pronouns_from_domain_async
26
+ from .error import PODNSError
27
+ from .parser import parse_pronoun_records
28
+ from .pronouns import PronounsResponse
29
+
30
+
31
+ __all__: tuple[str, ...] = (
32
+ "fetch_pronouns_from_domain_sync",
33
+ "fetch_pronouns_from_domain_async",
34
+ "PODNSError",
35
+ "parse_pronoun_records",
36
+ "PronounsResponse",
37
+ )
podns/dns.py ADDED
@@ -0,0 +1,51 @@
1
+ """
2
+ MIT License
3
+
4
+ Copyright (c) 2024-present abigail phoebe <abigail@phoebe.sh>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ """
24
+
25
+ import dns.asyncresolver
26
+ import dns.resolver
27
+
28
+ from podns.parser import parse_pronoun_records
29
+ from podns.pronouns import PronounsResponse
30
+
31
+
32
+ __all__: tuple[str, ...] = (
33
+ "fetch_pronouns_from_domain_sync",
34
+ "fetch_pronouns_from_domain_async",
35
+ )
36
+
37
+
38
+ def fetch_pronouns_from_domain_sync(domain: str, *, pedantic: bool = False) -> PronounsResponse | None:
39
+ try:
40
+ dns_answers = dns.resolver.resolve(f"pronouns.{domain}", "TXT")
41
+ except dns.resolver.NXDOMAIN:
42
+ return None
43
+ return parse_pronoun_records(list(dns_answers), pedantic=pedantic)
44
+
45
+
46
+ async def fetch_pronouns_from_domain_async(domain: str, *, pedantic: bool = False) -> PronounsResponse | None:
47
+ try:
48
+ dns_answers = await dns.asyncresolver.resolve(f"pronouns.{domain}", "TXT")
49
+ except dns.resolver.NXDOMAIN:
50
+ return None
51
+ return parse_pronoun_records(list(dns_answers), pedantic=pedantic)
podns/error.py ADDED
@@ -0,0 +1,76 @@
1
+ """
2
+ MIT License
3
+
4
+ Copyright (c) 2024-present abigail phoebe <abigail@phoebe.sh>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ """
24
+
25
+ __all__: tuple[str, ...] = (
26
+ "PODNSError",
27
+ "PODNSParserError",
28
+ "PODNSParserSetsAfterNone",
29
+ "PODNSParserInvalidTag",
30
+ "PODNSParserEmptySegmentInPronounSet",
31
+ "PODNSParserTagWithoutPronounSet",
32
+ "PODNSParserTrailingSlash",
33
+ "PODNSParserInsufficientPronounSetValues",
34
+ "PODNSParserIllegalCharacterInPronouns",
35
+ "PODNSParserTooManyPronounSetValues",
36
+ )
37
+
38
+
39
+ class PODNSError(Exception):
40
+ pass
41
+
42
+
43
+ class PODNSParserError(PODNSError):
44
+ pass
45
+
46
+
47
+ class PODNSParserSetsAfterNone(PODNSParserError):
48
+ pass
49
+
50
+
51
+ class PODNSParserInvalidTag(PODNSParserError):
52
+ pass
53
+
54
+
55
+ class PODNSParserEmptySegmentInPronounSet(PODNSParserError):
56
+ pass
57
+
58
+
59
+ class PODNSParserTagWithoutPronounSet(PODNSParserError):
60
+ pass
61
+
62
+
63
+ class PODNSParserTrailingSlash(PODNSParserError):
64
+ pass
65
+
66
+
67
+ class PODNSParserInsufficientPronounSetValues(PODNSParserError):
68
+ pass
69
+
70
+
71
+ class PODNSParserIllegalCharacterInPronouns(PODNSParserError):
72
+ pass
73
+
74
+
75
+ class PODNSParserTooManyPronounSetValues(PODNSParserError):
76
+ pass
podns/parser.py ADDED
@@ -0,0 +1,201 @@
1
+ """
2
+ MIT License
3
+
4
+ Copyright (c) 2024-present abigail phoebe <abigail@phoebe.sh>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ """
24
+
25
+ from typing import Final, Literal
26
+
27
+ from podns.error import (
28
+ PODNSParserEmptySegmentInPronounSet,
29
+ PODNSParserIllegalCharacterInPronouns,
30
+ PODNSParserInsufficientPronounSetValues,
31
+ PODNSParserInvalidTag,
32
+ PODNSParserSetsAfterNone,
33
+ PODNSParserTagWithoutPronounSet,
34
+ PODNSParserTooManyPronounSetValues,
35
+ PODNSParserTrailingSlash,
36
+ )
37
+ from podns.pronouns import (
38
+ PronounRecord,
39
+ Pronouns,
40
+ PronounsResponse,
41
+ PronounTag,
42
+ )
43
+
44
+
45
+ __all__: tuple[str, ...] = (
46
+ "parse_pronoun_records",
47
+ )
48
+
49
+
50
+ ILLEGAL_PRONOUN_CHARACTERS: Final[Literal[r'*;/!#']] = r'*;/!#'
51
+ PARSER_CONVERSIONS: Final[dict[str, str]] = {
52
+ "it/its": "it/it/its/its/itself",
53
+ }
54
+
55
+
56
+ def _normalise_record(record: str) -> str:
57
+ # remove comments, capitalisation and leading/trailing whitespace.
58
+ record = record.split("#")[0].lower().strip()
59
+
60
+ # remove spaces around /'s
61
+ pronoun_segment: str = record.split(";")[0]
62
+ pronoun_segments: list[str] = pronoun_segment.split("/")
63
+ pronoun_segment: str = '/'.join(seg.strip() for seg in pronoun_segments)
64
+
65
+ # ensure that there are no illegal characters present
66
+ illegal_characters_set: set[str] = set(ILLEGAL_PRONOUN_CHARACTERS)
67
+ for seg in pronoun_segments:
68
+ if illegal_characters_set.difference(seg) != illegal_characters_set:
69
+ raise PODNSParserIllegalCharacterInPronouns(f"{seg=} contains an illegal character defined by: {ILLEGAL_PRONOUN_CHARACTERS=}")
70
+
71
+ # add tag part back to record
72
+ tag_segment_parts: list[str] = record.split(";")
73
+ if len(tag_segment_parts) == 1:
74
+ full_record = pronoun_segment
75
+ else:
76
+ tag_segment: str = ';'.join(tag_segment_parts[1:])
77
+ full_record: str = pronoun_segment + ";" + tag_segment
78
+
79
+ # normalise repeating ;
80
+ _strip_chars: bool = False
81
+ stripped_record: str = ""
82
+ for char in full_record:
83
+ if char == ";" and not _strip_chars:
84
+ _strip_chars = True
85
+ stripped_record += char
86
+ elif char != ";" and _strip_chars:
87
+ _strip_chars = False
88
+ stripped_record += char
89
+ elif not _strip_chars:
90
+ stripped_record += char
91
+
92
+ return stripped_record
93
+
94
+
95
+ def _parse_pronouns(record, *, pedantic: bool) -> Pronouns:
96
+ # we do not care about the tag section here.
97
+ pronoun_part = record.split(";")[0]
98
+
99
+ if pronoun_part in PARSER_CONVERSIONS.keys():
100
+ pronoun_part = PARSER_CONVERSIONS[pronoun_part]
101
+
102
+ # check for trailing slash
103
+ if pronoun_part.endswith("/") and pedantic:
104
+ raise PODNSParserTrailingSlash(f"Trailing slash in pronouns with {pedantic=} - {record=}")
105
+
106
+ # check for empty segment
107
+ pronouns_set = pronoun_part.split("/")
108
+ if any(True for p in pronouns_set if len(p) == 0):
109
+ raise PODNSParserEmptySegmentInPronounSet(f"Empty pronoun segment: {record=}")
110
+
111
+ # ensure at least subject/object are present
112
+ if len(pronouns_set) < 2:
113
+ raise PODNSParserInsufficientPronounSetValues(f"There must be at least subject and object pronouns: {record=}")
114
+
115
+ if len(pronouns_set) > 5 and pedantic:
116
+ raise PODNSParserTooManyPronounSetValues(f"There are too many provided pronoun values: {record=}")
117
+
118
+ # appropriately parse into a `Pronouns` object
119
+ possessive_determiner = None
120
+ if len(pronouns_set) > 2:
121
+ possessive_determiner = pronouns_set[2]
122
+
123
+ possessive_pronoun = None
124
+ if len(pronouns_set) > 3:
125
+ possessive_pronoun = pronouns_set[3]
126
+
127
+ reflexive = None
128
+ if len(pronouns_set) > 4:
129
+ reflexive = pronouns_set[4]
130
+
131
+ return Pronouns(
132
+ subject=pronouns_set[0],
133
+ object=pronouns_set[1],
134
+ possessive_determiner=possessive_determiner,
135
+ possessive_pronoun=possessive_pronoun,
136
+ reflexive=reflexive,
137
+ )
138
+
139
+
140
+ def _parse_tags(record, *, pedantic: bool) -> set[PronounTag]:
141
+ parts = record.rsplit(";")
142
+ if len(parts) == 1 and ";" in record:
143
+ if pedantic:
144
+ raise PODNSParserTagWithoutPronounSet(f"A tag was defined without a preceding pronoun set declaration: {record=}")
145
+ else:
146
+ return set()
147
+
148
+ pronoun_tags: set[PronounTag] = set()
149
+ parsed_tags = set(parts[1:]).difference("")
150
+ for parsed_tag in parsed_tags:
151
+ try:
152
+ pronoun_tags.add(PronounTag(parsed_tag))
153
+ except ValueError:
154
+ if pedantic:
155
+ raise PODNSParserInvalidTag(f"Invalid tag: {parsed_tag=}")
156
+
157
+ return pronoun_tags
158
+
159
+
160
+ def _parse_record(record: str, *, pedantic: bool) -> PronounRecord:
161
+ # parse the pronouns and tags part into an appropriate record
162
+ pronouns: Pronouns = _parse_pronouns(record, pedantic=pedantic)
163
+ tags: set[PronounTag] = _parse_tags(record, pedantic=pedantic)
164
+
165
+ if ( # specifically mark they/them as plural
166
+ pronouns.subject == "they"
167
+ and pronouns.object == "them"
168
+ and PronounTag.PLURAL not in tags
169
+ ):
170
+ tags.add(PronounTag.PLURAL)
171
+
172
+ return PronounRecord(pronouns=pronouns, tags=tags)
173
+
174
+
175
+ def parse_pronoun_records(pronoun_records: list[str], *, pedantic: bool = False) -> PronounsResponse:
176
+ uses_any_pronouns: bool = False
177
+ uses_name_only: bool = False
178
+ records: list[PronounRecord] = []
179
+
180
+ for record in pronoun_records:
181
+ normalised_record: str = _normalise_record(record)
182
+ if len(normalised_record) == 0: # empty record
183
+ continue
184
+ elif normalised_record.startswith("!"): # none; use name only declarator
185
+ uses_name_only = True
186
+ continue
187
+ elif normalised_record.startswith("*"): # wildcard; any pronoun is declarator
188
+ uses_any_pronouns = True
189
+ continue
190
+ else: # pronoun set
191
+ parsed_record: PronounRecord = _parse_record(normalised_record, pedantic=pedantic)
192
+ records.append(parsed_record)
193
+
194
+ if pedantic and uses_name_only and len(records) > 0:
195
+ raise PODNSParserSetsAfterNone(f"{uses_name_only=} and {len(records) > 0=}.")
196
+
197
+ return PronounsResponse(
198
+ uses_any_pronouns=uses_any_pronouns,
199
+ uses_name_only=uses_name_only,
200
+ records=[] if uses_name_only else records,
201
+ )
podns/pronouns.py ADDED
@@ -0,0 +1,60 @@
1
+ """
2
+ MIT License
3
+
4
+ Copyright (c) 2024-present abigail phoebe <abigail@phoebe.sh>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ """
24
+
25
+ from dataclasses import dataclass
26
+ from enum import StrEnum
27
+
28
+
29
+ __all__: tuple[str, ...] = (
30
+ "PronounsResponse",
31
+ "PronounTag",
32
+ "Pronouns",
33
+ )
34
+
35
+
36
+ class PronounTag(StrEnum):
37
+ PREFERRED = "preferred"
38
+ PLURAL = "plural"
39
+
40
+
41
+ @dataclass(slots=True, frozen=True)
42
+ class Pronouns:
43
+ subject: str
44
+ object: str
45
+ possessive_determiner: str | None
46
+ possessive_pronoun: str | None
47
+ reflexive: str | None
48
+
49
+
50
+ @dataclass(slots=True, frozen=True)
51
+ class PronounRecord:
52
+ pronouns: Pronouns
53
+ tags: set[PronounTag]
54
+
55
+
56
+ @dataclass(slots=True, frozen=True)
57
+ class PronounsResponse:
58
+ uses_any_pronouns: bool
59
+ uses_name_only: bool
60
+ records: list[PronounRecord]
podns/py.typed ADDED
File without changes
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: podns
3
+ Version: 0.1.1
4
+ Summary: A Pronouns over DNS specification compliant Python API.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: abigail phoebe
8
+ Author-email: abigail@phoebe.sh
9
+ Requires-Python: >=3.14
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: dnspython (>=2.8.0)
14
+ Project-URL: Bug Tracker, https://github.com/ijsbol/podns_py/issues
15
+ Project-URL: Homepage, https://github.com/ijsbol/podns_py
16
+ Description-Content-Type: text/markdown
17
+
18
+ # podns_py
19
+
20
+ A [Pronouns over DNS](https://github.com/CutieZone/pronouns-over-dns/) specification compliant Python API.
21
+
22
+
23
+ ## Documentation
24
+
25
+ ### Fetching from domain record.
26
+
27
+ If you want to fetch someones pronouns from their domain, see the below:
28
+
29
+ Note that we support both synchronous and asynchronous lookups.
30
+
31
+
32
+ ```python
33
+ import asyncio
34
+
35
+ import podns.dns
36
+
37
+
38
+ async def main() -> None:
39
+ domain = "abigail.sh"
40
+ podns.dns.fetch_pronouns_from_domain_sync(domain)
41
+ await podns.dns.fetch_pronouns_from_domain_async(domain)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ asyncio.run(main())
46
+ ```
47
+
48
+ ### Parsing a raw list
49
+
50
+ If you already have fetched the users pronouns, or are just parsing a raw literal:
51
+
52
+ ```python
53
+ import podns.parser
54
+
55
+ podns.parser.parse_pronoun_records([
56
+ "she/her",
57
+ "they/them/theirs;preferred"
58
+ ])
59
+ ```
60
+
61
+ ### Optional pedantic `kwarg` on user APIs
62
+
63
+ For all user-level APIs (that is, `podns.dns.fetch_pronouns_from_domain_*` and `podns.parser.parse_pronoun_records`), there is an optional kwarg, `pedantic`, that defaults to `False`.
64
+
65
+ - Setting this to `True` will raise errors on all specification-violating parse errors from the provided records.
66
+ - Setting this to `False` will only raise errors on egregious specification violations that make parsing impossible. When set to `False`, podns will do its' best to infer intent when met with trivial violations, it will however never return a `podns.pronouns.PronounsResponse` that violates the specification.
67
+
@@ -0,0 +1,10 @@
1
+ podns/__init__.py,sha256=kMQV4rZozSEsiUq7mYsziyYgfsX3VG87jBYRqKCXL50,1485
2
+ podns/dns.py,sha256=zGLPEvamxjRdZCFTvrEhO1Qa4QVZ5UBpaWNqNpyO264,1999
3
+ podns/error.py,sha256=k1L5jybcQ-bdels_wsJz-1oRMrPrlyURJGcgw8ElkUs,2118
4
+ podns/parser.py,sha256=jMypxkVDauybcAPHUma_DG6R_-mqEeUH2F82aPhSVjA,7252
5
+ podns/pronouns.py,sha256=hcH0ZIi0SjniE3Aq53HgDu5NQF-op6Q-vaKeSyh0B68,1779
6
+ podns/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ podns-0.1.1.dist-info/METADATA,sha256=-02_Yf6CjVSTt_F69X-DeLi2_OEdgeoacZ8gi9hAA_4,2049
8
+ podns-0.1.1.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
9
+ podns-0.1.1.dist-info/licenses/LICENSE,sha256=wcpZDgCp7rR4gv4QLE-rmgeYYEyX3-MO9RDyGxldD0s,1099
10
+ podns-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.3.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 abigail phoebe ୧ ‧₊˚ 🍵 ⋅ᰔᩚ
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.