autonomous-app 0.3.18__py3-none-any.whl → 0.3.20__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.
@@ -4,6 +4,7 @@ import pymongo
4
4
  from bson import SON, ObjectId
5
5
  from bson.dbref import DBRef
6
6
 
7
+ from autonomous import log
7
8
  from autonomous.db.base import UPDATE_OPERATORS
8
9
  from autonomous.db.common import _import_class
9
10
  from autonomous.db.errors import InvalidQueryError
@@ -76,14 +77,18 @@ def query(_doc_cls=None, **kwargs):
76
77
  """Transform a query from Django-style format to Mongo format."""
77
78
  mongo_query = {}
78
79
  merge_query = defaultdict(list)
80
+
81
+ # Iterate over sorted keyword arguments
79
82
  for key, value in sorted(kwargs.items()):
80
83
  if key == "__raw__":
81
84
  handle_raw_query(value, mongo_query)
82
85
  continue
83
86
 
87
+ # Split the key into parts based on '__'
84
88
  parts = key.rsplit("__")
85
89
  indices = [(i, p) for i, p in enumerate(parts) if p.isdigit()]
86
90
  parts = [part for part in parts if not part.isdigit()]
91
+
87
92
  # Check for an operator and transform to mongo-style if there is
88
93
  op = None
89
94
  if len(parts) > 1 and parts[-1] in MATCH_OPERATORS:
@@ -93,6 +98,7 @@ def query(_doc_cls=None, **kwargs):
93
98
  if len(parts) > 1 and parts[-1] == "":
94
99
  parts.pop()
95
100
 
101
+ # Check for negation
96
102
  negate = False
97
103
  if len(parts) > 1 and parts[-1] == "not":
98
104
  parts.pop()
@@ -115,7 +121,7 @@ def query(_doc_cls=None, **kwargs):
115
121
  if isinstance(field, str):
116
122
  parts.append(field)
117
123
  append_field = False
118
- # is last and CachedReferenceField
124
+ # Handle CachedReferenceField
119
125
  elif isinstance(field, CachedReferenceField) and fields[-1] == field:
120
126
  parts.append("%s._id" % field.db_field)
121
127
  else:
@@ -139,17 +145,14 @@ def query(_doc_cls=None, **kwargs):
139
145
  # Raise an error if the in/nin/all/near param is not iterable.
140
146
  value = _prepare_query_for_iterable(field, op, value)
141
147
 
142
- # If we're querying a GenericReferenceField, we need to alter the
143
- # key depending on the value:
144
- # * If the value is a DBRef, the key should be "field_name._ref".
145
- # * If the value is an ObjectId, the key should be "field_name._ref.$id".
148
+ # Handle GenericReferenceField
146
149
  if isinstance(field, GenericReferenceField):
147
150
  if isinstance(value, DBRef):
148
151
  parts[-1] += "._ref"
149
152
  elif isinstance(value, ObjectId):
150
153
  parts[-1] += "._ref.$id"
151
154
 
152
- # if op and op not in COMPARISON_OPERATORS:
155
+ # Handle different operators
153
156
  if op:
154
157
  if op in GEO_OPERATORS:
155
158
  value = _geo_operator(field, op, value)
@@ -166,9 +169,7 @@ def query(_doc_cls=None, **kwargs):
166
169
  value = field.prepare_query_value(op, value)
167
170
  value = {"$elemMatch": value}
168
171
  elif op in CUSTOM_OPERATORS:
169
- NotImplementedError(
170
- 'Custom method "%s" has not ' "been implemented" % op
171
- )
172
+ NotImplementedError('Custom method "%s" has not been implemented' % op)
172
173
  elif op not in STRING_OPERATORS:
173
174
  value = {"$" + op: value}
174
175
 
@@ -439,7 +440,7 @@ def _geo_operator(field, op, value):
439
440
  value = {"$within": {"$box": value}}
440
441
  else:
441
442
  raise NotImplementedError(
442
- 'Geo method "%s" has not been ' "implemented for a GeoPointField" % op
443
+ 'Geo method "%s" has not been implemented for a GeoPointField' % op
443
444
  )
444
445
  else:
445
446
  if op == "geo_within":
@@ -64,6 +64,13 @@ class ReferenceAttr(GenericReferenceField):
64
64
  class ListAttr(ListField):
65
65
  def __get__(self, instance, owner):
66
66
  results = super().__get__(instance, owner)
67
+
68
+ # sanity check
69
+ if not isinstance(results, list):
70
+ super().__set__(instance, [])
71
+ results = super().__get__(instance, owner)
72
+
73
+ # log(f"ListAttr: {results}")
67
74
  if isinstance(self.field, ReferenceAttr):
68
75
  i = 0
69
76
  while i < len(results):
@@ -78,9 +85,6 @@ class ListAttr(ListField):
78
85
  # log(f"Object Not Found: {results[i]}")
79
86
  return results
80
87
 
81
- # def append(self, obj):
82
- # results = super().__get__(instance, owner) or []
83
-
84
88
 
85
89
  class DictAttr(DictField):
86
90
  def __get__(self, instance, owner):
@@ -96,6 +100,16 @@ class DictAttr(DictField):
96
100
  results[key] = lazy_obj
97
101
  return results
98
102
 
103
+ # def __set__(self, instance, value):
104
+ # import traceback
105
+
106
+ # traceback.print_stack()
107
+
108
+ # log(value, instance.player_messages, _print=True)
109
+ # result = super().__set__(instance, value) or {}
110
+ # log(value, instance.player_messages, _print=True)
111
+ # return result
112
+
99
113
 
100
114
  class EnumAttr(EnumField):
101
115
  pass
@@ -1,4 +1,3 @@
1
- import importlib
2
1
  import os
3
2
  import urllib.parse
4
3
  from datetime import datetime
@@ -17,7 +16,6 @@ username = urllib.parse.quote_plus(str(os.getenv("DB_USERNAME")))
17
16
  dbname = os.getenv("DB_DB")
18
17
  # log(f"Connecting to MongoDB at {host}:{port} with {username}:{password} for {dbname}")
19
18
  connect(host=f"mongodb://{username}:{password}@{host}:{port}/{dbname}?authSource=admin")
20
- # log(f"{db}")
21
19
 
22
20
 
23
21
  class AutoModel(Document):
@@ -25,13 +23,13 @@ class AutoModel(Document):
25
23
  last_updated = DateTimeField(default=datetime.now)
26
24
 
27
25
  def __eq__(self, other):
28
- return self.pk == other.pk if other else False
26
+ return str(self.pk) == str(other.pk) if other else False
29
27
 
30
28
  def __lt__(self, other):
31
- return self.pk < other.pk if other else False
29
+ return str(self.pk) < str(other.pk) if other else False
32
30
 
33
31
  def __gt__(self, other):
34
- return not (self.pk < other.pk) if other else False
32
+ return not (str(self.pk) < str(other.pk)) if other else False
35
33
 
36
34
  def __le__(self, other):
37
35
  return self < other or self == other
@@ -88,19 +86,31 @@ class AutoModel(Document):
88
86
  f"{self.__module__}.{self._class_name}" if qualified else self._class_name
89
87
  )
90
88
 
89
+ @classmethod
90
+ def get_model(cls, model, pk=None):
91
+ try:
92
+ Model = cls.load_model(model)
93
+ except ValueError:
94
+ Model = None
95
+ return Model.get(pk) if Model and pk else Model
96
+
91
97
  @classmethod
92
98
  def load_model(cls, model):
93
99
  if not isinstance(model, str):
94
100
  return model
95
101
 
96
102
  subclasses = AutoModel.__subclasses__()
103
+ visited_subclasses = []
97
104
  while subclasses:
105
+ # log(subclasses, _print=True)
98
106
  subclass = subclasses.pop()
99
107
  if "_meta" in subclass.__dict__ and not subclass._meta.get("abstract"):
108
+ # log(f"Checking {subclass.__name__}", _print=True)
100
109
  if subclass.__name__.lower() == model.lower():
101
110
  return subclass
102
- elif subclass not in subclasses:
103
- subclasses += [subclass]
111
+ if subclass not in visited_subclasses:
112
+ subclasses += subclass.__subclasses__()
113
+ visited_subclasses += [subclass]
104
114
  raise ValueError(f"Model {model} not found")
105
115
 
106
116
  @classmethod
@@ -123,16 +133,21 @@ class AutoModel(Document):
123
133
  elif isinstance(pk, dict) and "$oid" in pk:
124
134
  pk = bson.ObjectId(pk["$oid"])
125
135
  try:
126
- return cls.objects.get(id=pk)
136
+ # log(pk, type(pk))
137
+ result = cls.objects.get(id=pk)
138
+ # log(result)
127
139
  except cls.DoesNotExist as e:
128
140
  log(f"Model {cls.__name__} with pk {pk} not found : {e}")
129
141
  return None
130
142
  except ValidationError as e:
143
+ # traceback.print_stack(limit=5)
131
144
  log(f"Model Validation failure {cls.__name__} [{pk}]: {e}")
132
145
  return None
133
146
  except Exception as e:
134
147
  log(f"Error getting model {cls.__name__} with pk {pk}: {e}", _print=True)
135
148
  raise e
149
+ else:
150
+ return result
136
151
 
137
152
  @classmethod
138
153
  def random(cls):
@@ -223,7 +238,6 @@ class AutoModel(Document):
223
238
  Returns:
224
239
  int: The primary key (pk) of the saved model.
225
240
  """
226
- # log(self.to_json())
227
241
  obj = super().save()
228
242
  self.pk = obj.pk
229
243
  return self.pk
@@ -1,40 +1,19 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: autonomous-app
3
- Version: 0.3.18
3
+ Version: 0.3.20
4
4
  Summary: Containerized application framework built on Flask with additional libraries and tools for rapid development of web applications.
5
5
  Author-email: Steven A Moore <samoore@binghamton.edu>
6
- License: MIT License
7
-
8
- Copyright (c) [2022] Steven Allen Moore
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in all
18
- copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- SOFTWARE.
27
6
  Project-URL: homepage, https://github.com/Sallenmoore/autonomous
28
7
  Classifier: Programming Language :: Python :: 3.12
29
8
  Classifier: License :: OSI Approved :: MIT License
30
9
  Classifier: Operating System :: OS Independent
31
10
  Requires-Python: >=3.12
32
11
  Description-Content-Type: text/markdown
33
- License-File: LICENSE
34
12
  Requires-Dist: Flask
35
13
  Requires-Dist: setuptools
36
14
  Requires-Dist: python-dotenv
37
15
  Requires-Dist: blinker
16
+ Requires-Dist: pymongo
38
17
  Requires-Dist: PyGithub
39
18
  Requires-Dist: pygit2
40
19
  Requires-Dist: pillow
@@ -44,9 +23,12 @@ Requires-Dist: requests
44
23
  Requires-Dist: gunicorn
45
24
  Requires-Dist: Authlib
46
25
  Requires-Dist: rq
26
+ Requires-Dist: ollama
47
27
  Requires-Dist: openai>=1.42
28
+ Requires-Dist: google-genai
48
29
  Requires-Dist: dateparser
49
30
  Requires-Dist: python-slugify
31
+ Requires-Dist: pydub
50
32
 
51
33
  # Autonomous
52
34
 
@@ -1,14 +1,18 @@
1
- autonomous/__init__.py,sha256=1pYiGqqSX2X8kVRPUaevzeWWPs21MIOrdfy-91-Nrkg,95
1
+ autonomous/__init__.py,sha256=lgBgCoMYcfqa2rFXXDlQXY47095nfXVNXxXwNWYDzYU,95
2
2
  autonomous/cli.py,sha256=z4AaGeWNW_uBLFAHng0J_lfS9v3fXemK1PeT85u4Eo4,42
3
3
  autonomous/logger.py,sha256=NQtgEaTWNAWfLSgqSP7ksXj1GpOuCgoUV711kSMm-WA,2022
4
4
  autonomous/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- autonomous/ai/audioagent.py,sha256=PlDNOdYKJxsqWbql0aAv_F7dS0piBeZV3nuKKZ0-hAI,613
6
- autonomous/ai/baseagent.py,sha256=gXOdfAWokEf3P7z2jQlbHmwSZohvXfdapZfkoLz6HuQ,1073
5
+ autonomous/ai/audioagent.py,sha256=9QsA-cwj3P4j730bvzUJ3nbucU8tsNLYk_swbkapVvI,966
6
+ autonomous/ai/baseagent.py,sha256=HYCqC4HmK5afNMunmTkhRE8O0OaONl2GxXnISkdOM58,1094
7
7
  autonomous/ai/imageagent.py,sha256=bIOrgg_CM-rgfyLme7V9vPqP8WKVMIAVoB2E9lLtIRk,521
8
- autonomous/ai/jsonagent.py,sha256=wlTULEBlrAAb8ELMxik1b7YOgHD4ARmbWLUWc5pgxB8,1197
9
- autonomous/ai/textagent.py,sha256=8iVuY-cSJsJgyWKrpCoj46c0MIsB13PFPNYdxSyqlKI,1096
8
+ autonomous/ai/jsonagent.py,sha256=a_l4HyyVRj3FB6py_P1xdc4Bj9uNI1YmJrWQXAksIvs,964
9
+ autonomous/ai/textagent.py,sha256=wI1-VC9zscKYyxYBg4pZ0ZyNJ5ZvKkLfWsIY1vJFChk,863
10
10
  autonomous/ai/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- autonomous/ai/models/openai.py,sha256=ih02eSWWDNPpbmIlc49HB8xHpZcEKi_GnbIEHhUlvaA,12448
11
+ autonomous/ai/models/aws.py,sha256=bGDjnGTm350zOqor9IsICzUkBUN2bubGI_ZssQuSXIw,12715
12
+ autonomous/ai/models/deepseek.py,sha256=fkoi-hJp60yFlZ9Cb9PdUrmNSErYltQ5ezkUI75llXc,2734
13
+ autonomous/ai/models/gemini.py,sha256=XPnja5_gp6sNM5k8oR_kMHMafKKe_vbtiutoSWKqNr4,10890
14
+ autonomous/ai/models/local.py,sha256=fkoi-hJp60yFlZ9Cb9PdUrmNSErYltQ5ezkUI75llXc,2734
15
+ autonomous/ai/models/openai.py,sha256=2-LttCm6woGklaLbs1H5LjlbfM-7leDwGmC9vksSqW4,13135
12
16
  autonomous/apis/version_control/GHCallbacks.py,sha256=AyiUlYfV5JePi11GVyqYyXoj5UTbPKzS-HRRI94rjJo,1069
13
17
  autonomous/apis/version_control/GHOrganization.py,sha256=mi2livdsGurKiifbvuLwiFbdDzL77IlEfhwEa-tG77I,1155
14
18
  autonomous/apis/version_control/GHRepo.py,sha256=hTFHMkxSbSlVELfh8S6mq6ijkIKPRQO-Q5775ZjRKD4,4622
@@ -26,7 +30,7 @@ autonomous/db/context_managers.py,sha256=_nH2ajCL8Xy90AuB2rKaryR4iF8Q8ksU3Nei_mZ
26
30
  autonomous/db/dereference.py,sha256=EgbpPCXtDZqD_ZuY1Wd4o3ltRy8qEo3C5yRh5_c9fLE,12776
27
31
  autonomous/db/document.py,sha256=oZKdTaoqwv9fCHiv450rIxgINASQF3J9FzIsUOUXHhw,44428
28
32
  autonomous/db/errors.py,sha256=_QeCotid1kmr7_W0QyH6NUrwwYN9eced_yyyiop0Xlw,4108
29
- autonomous/db/fields.py,sha256=YMeXO5C8hiWKrknmQtaWOKDF-b1pMVEvCnlZrjMBamE,93568
33
+ autonomous/db/fields.py,sha256=2SrsdCXQYnNg9bPfKs9CGa1mTnu6cQFqu0KyFPSamNA,93551
30
34
  autonomous/db/mongodb_support.py,sha256=u0X-zpqTIZZP8o2-IDyKRKHL8ALLhvW1VSGtK3fLyos,626
31
35
  autonomous/db/pymongo_support.py,sha256=UEZ4RHAGb_t1nuMUAJXMNs0vdH3dutxAH5mwFCmG6jI,2951
32
36
  autonomous/db/signals.py,sha256=BM-M4hh4SrTbV9bZVIEWTG8mxgKn9Lo2rC7owLJz4yQ,1791
@@ -38,23 +42,22 @@ autonomous/db/base/fields.py,sha256=NorRXWR_NAJ1D2XkuICfEB6tM977-AaboYl3R2uSy8E,
38
42
  autonomous/db/base/metaclasses.py,sha256=GVvJYcCxaW1ltEqyH4oNvT_srckEXDSHOtHVU_TAN70,18138
39
43
  autonomous/db/base/utils.py,sha256=MH4FuEwh-5IcIinwNTkyTs-PqQLyyiMctcYMsNP85qk,617
40
44
  autonomous/db/queryset/__init__.py,sha256=XT3__0BJCvQIQj3S_Mp7mPxNBkfdvXkdw56cg2gc86o,756
41
- autonomous/db/queryset/base.py,sha256=RjxzR55i7pfj9yntMZ_dSTbXp0Ik6HA57tdQFCWXXk8,76103
45
+ autonomous/db/queryset/base.py,sha256=unJiIEwh7dtZblYeLHCvq6QkXm8CYsm09bMnPkNJUYc,76040
42
46
  autonomous/db/queryset/field_list.py,sha256=qY50kgMYzloZXrOXnWT0PS_fBJCoThSioRvW9-HmhYA,2964
43
47
  autonomous/db/queryset/manager.py,sha256=fXu95TlGChdJWTRA4OnY_Ik25JzezJ2_qPqmH78xJsY,2238
44
- autonomous/db/queryset/queryset.py,sha256=Xvt1q7Olij9STbJkHm6dKrUIrgyJeY_uwJOYE9WUZvk,5942
45
- autonomous/db/queryset/transform.py,sha256=UhBdDxYR_bWH0ECnaSw9g9YMwgWRZtsRl_q6PkqO9eY,19615
48
+ autonomous/db/queryset/queryset.py,sha256=SRLYWAQUXgcfNzeMPiH5Mm4WZIemHTNQ24y2EIisNQU,5969
49
+ autonomous/db/queryset/transform.py,sha256=IIZKf_io60zPTIwJ5KcPcrJOOOOjD2yQU7coYClL1Iw,19461
46
50
  autonomous/db/queryset/visitor.py,sha256=AN09lR6hWYUlKJC7G1sktvnWy5hrFnpoQhi58bOXbA4,5470
47
51
  autonomous/model/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
48
- autonomous/model/autoattr.py,sha256=eqO3QE17EMRzLvtF74gXefeK1I2N-w6uScpibdChVOw,2254
49
- autonomous/model/automodel.py,sha256=YNye6Iay6zZF8upNCAmrShQjotZhtAuYBV7v_dgRRyk,7627
52
+ autonomous/model/autoattr.py,sha256=FUnQrw65CcZumYiTsQ7U6G6UDGqbeekka-cjz6Sfchc,2675
53
+ autonomous/model/automodel.py,sha256=F9rlsna1QYg8mVb-5ErKx5fEXxvaogVxWeeaJQBOOjs,8166
50
54
  autonomous/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
55
  autonomous/storage/imagestorage.py,sha256=SmBjBNBlP1ZEjxdOnGVzCHZhbEhMKTUQC2TbpWbejDE,6168
52
56
  autonomous/storage/localstorage.py,sha256=FzrR6O9mMGAZt5dDgqzkeOQVfGRXCygR0kksz2MPpwE,2286
53
57
  autonomous/tasks/__init__.py,sha256=pn7iZ14MhcHUdzcLkfkd4-45wgPP0tXahAz_cFgb_Tg,32
54
58
  autonomous/tasks/autotask.py,sha256=aK5iapDhgcAic3F5ZYMAhNKJkOepj8yWwbMizKDzUwQ,4153
55
59
  autonomous/utils/markdown.py,sha256=tf8vlHARiQO1X_aGbqlYozzP_TbdiDRT9EEP6aFRQo0,2153
56
- autonomous_app-0.3.18.dist-info/LICENSE,sha256=-PHHSuDRkodHo3PEdMkDtoIdmLAOomMq6lsLaOetU8g,1076
57
- autonomous_app-0.3.18.dist-info/METADATA,sha256=4pBmaY1mtfqdPWXwq47l0f_iwtfjcPefoLn_XM7ZFjA,4189
58
- autonomous_app-0.3.18.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
59
- autonomous_app-0.3.18.dist-info/top_level.txt,sha256=ZyxWWDdbvZekF3UFunxl4BQsVDb_FOW3eTn0vun_jb4,11
60
- autonomous_app-0.3.18.dist-info/RECORD,,
60
+ autonomous_app-0.3.20.dist-info/METADATA,sha256=GKNdh8UqT-1bTYkNOoWmlrfZz-UORszoXkcHwSZRn68,3015
61
+ autonomous_app-0.3.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
62
+ autonomous_app-0.3.20.dist-info/top_level.txt,sha256=ZyxWWDdbvZekF3UFunxl4BQsVDb_FOW3eTn0vun_jb4,11
63
+ autonomous_app-0.3.20.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) [2022] Steven Allen Moore
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.