gamsapi 52.5.0__cp312-cp312-win_amd64.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 (257) hide show
  1. gams/__init__.py +27 -0
  2. gams/_version.py +1 -0
  3. gams/connect/__init__.py +28 -0
  4. gams/connect/agents/__init__.py +24 -0
  5. gams/connect/agents/_excel/__init__.py +32 -0
  6. gams/connect/agents/_excel/excelagent.py +312 -0
  7. gams/connect/agents/_excel/workbook.py +155 -0
  8. gams/connect/agents/_sqlconnectors/__init__.py +42 -0
  9. gams/connect/agents/_sqlconnectors/_accesshandler.py +211 -0
  10. gams/connect/agents/_sqlconnectors/_databasehandler.py +250 -0
  11. gams/connect/agents/_sqlconnectors/_mysqlhandler.py +168 -0
  12. gams/connect/agents/_sqlconnectors/_postgreshandler.py +131 -0
  13. gams/connect/agents/_sqlconnectors/_pyodbchandler.py +112 -0
  14. gams/connect/agents/_sqlconnectors/_sqlalchemyhandler.py +74 -0
  15. gams/connect/agents/_sqlconnectors/_sqlitehandler.py +262 -0
  16. gams/connect/agents/_sqlconnectors/_sqlserverhandler.py +179 -0
  17. gams/connect/agents/concatenate.py +440 -0
  18. gams/connect/agents/connectagent.py +743 -0
  19. gams/connect/agents/csvreader.py +675 -0
  20. gams/connect/agents/csvwriter.py +151 -0
  21. gams/connect/agents/domainwriter.py +143 -0
  22. gams/connect/agents/excelreader.py +756 -0
  23. gams/connect/agents/excelwriter.py +467 -0
  24. gams/connect/agents/filter.py +223 -0
  25. gams/connect/agents/gamsreader.py +112 -0
  26. gams/connect/agents/gamswriter.py +239 -0
  27. gams/connect/agents/gdxreader.py +109 -0
  28. gams/connect/agents/gdxwriter.py +146 -0
  29. gams/connect/agents/labelmanipulator.py +303 -0
  30. gams/connect/agents/projection.py +539 -0
  31. gams/connect/agents/pythoncode.py +71 -0
  32. gams/connect/agents/rawcsvreader.py +248 -0
  33. gams/connect/agents/rawexcelreader.py +312 -0
  34. gams/connect/agents/schema/CSVReader.yaml +92 -0
  35. gams/connect/agents/schema/CSVWriter.yaml +44 -0
  36. gams/connect/agents/schema/Concatenate.yaml +52 -0
  37. gams/connect/agents/schema/DomainWriter.yaml +25 -0
  38. gams/connect/agents/schema/ExcelReader.yaml +121 -0
  39. gams/connect/agents/schema/ExcelWriter.yaml +78 -0
  40. gams/connect/agents/schema/Filter.yaml +74 -0
  41. gams/connect/agents/schema/GAMSReader.yaml +20 -0
  42. gams/connect/agents/schema/GAMSWriter.yaml +47 -0
  43. gams/connect/agents/schema/GDXReader.yaml +23 -0
  44. gams/connect/agents/schema/GDXWriter.yaml +32 -0
  45. gams/connect/agents/schema/LabelManipulator.yaml +99 -0
  46. gams/connect/agents/schema/Projection.yaml +24 -0
  47. gams/connect/agents/schema/PythonCode.yaml +6 -0
  48. gams/connect/agents/schema/RawCSVReader.yaml +34 -0
  49. gams/connect/agents/schema/RawExcelReader.yaml +42 -0
  50. gams/connect/agents/schema/SQLReader.yaml +75 -0
  51. gams/connect/agents/schema/SQLWriter.yaml +103 -0
  52. gams/connect/agents/sqlreader.py +301 -0
  53. gams/connect/agents/sqlwriter.py +276 -0
  54. gams/connect/connectdatabase.py +275 -0
  55. gams/connect/connectvalidator.py +93 -0
  56. gams/connect/errors.py +34 -0
  57. gams/control/__init__.py +136 -0
  58. gams/control/database.py +2231 -0
  59. gams/control/execution.py +1900 -0
  60. gams/control/options.py +2792 -0
  61. gams/control/workspace.py +1198 -0
  62. gams/core/__init__.py +24 -0
  63. gams/core/cfg/__init__.py +26 -0
  64. gams/core/cfg/_cfgmcc.cp312-win_amd64.pyd +0 -0
  65. gams/core/cfg/cfgmcc.py +519 -0
  66. gams/core/dct/__init__.py +26 -0
  67. gams/core/dct/_dctmcc.cp312-win_amd64.pyd +0 -0
  68. gams/core/dct/dctmcc.py +574 -0
  69. gams/core/embedded/__init__.py +26 -0
  70. gams/core/embedded/gamsemb.py +1024 -0
  71. gams/core/emp/__init__.py +24 -0
  72. gams/core/emp/emplexer.py +89 -0
  73. gams/core/emp/empyacc.py +281 -0
  74. gams/core/gdx/__init__.py +26 -0
  75. gams/core/gdx/_gdxcc.cp312-win_amd64.pyd +0 -0
  76. gams/core/gdx/gdxcc.py +866 -0
  77. gams/core/gev/__init__.py +26 -0
  78. gams/core/gev/_gevmcc.cp312-win_amd64.pyd +0 -0
  79. gams/core/gev/gevmcc.py +855 -0
  80. gams/core/gmd/__init__.py +26 -0
  81. gams/core/gmd/_gmdcc.cp312-win_amd64.pyd +0 -0
  82. gams/core/gmd/gmdcc.py +917 -0
  83. gams/core/gmo/__init__.py +26 -0
  84. gams/core/gmo/_gmomcc.cp312-win_amd64.pyd +0 -0
  85. gams/core/gmo/gmomcc.py +2046 -0
  86. gams/core/idx/__init__.py +26 -0
  87. gams/core/idx/_idxcc.cp312-win_amd64.pyd +0 -0
  88. gams/core/idx/idxcc.py +510 -0
  89. gams/core/numpy/__init__.py +29 -0
  90. gams/core/numpy/_gams2numpy.cp312-win_amd64.pyd +0 -0
  91. gams/core/numpy/gams2numpy.py +1048 -0
  92. gams/core/opt/__init__.py +26 -0
  93. gams/core/opt/_optcc.cp312-win_amd64.pyd +0 -0
  94. gams/core/opt/optcc.py +840 -0
  95. gams/engine/__init__.py +204 -0
  96. gams/engine/api/__init__.py +13 -0
  97. gams/engine/api/auth_api.py +7653 -0
  98. gams/engine/api/cleanup_api.py +751 -0
  99. gams/engine/api/default_api.py +887 -0
  100. gams/engine/api/hypercube_api.py +2629 -0
  101. gams/engine/api/jobs_api.py +5229 -0
  102. gams/engine/api/licenses_api.py +2220 -0
  103. gams/engine/api/namespaces_api.py +7783 -0
  104. gams/engine/api/usage_api.py +5627 -0
  105. gams/engine/api/users_api.py +5931 -0
  106. gams/engine/api_client.py +804 -0
  107. gams/engine/api_response.py +21 -0
  108. gams/engine/configuration.py +601 -0
  109. gams/engine/exceptions.py +216 -0
  110. gams/engine/models/__init__.py +86 -0
  111. gams/engine/models/bad_input.py +89 -0
  112. gams/engine/models/cleanable_job_result.py +104 -0
  113. gams/engine/models/cleanable_job_result_page.py +113 -0
  114. gams/engine/models/engine_license.py +107 -0
  115. gams/engine/models/files_not_found.py +93 -0
  116. gams/engine/models/forwarded_token_response.py +112 -0
  117. gams/engine/models/generic_key_value_pair.py +89 -0
  118. gams/engine/models/hypercube.py +160 -0
  119. gams/engine/models/hypercube_page.py +111 -0
  120. gams/engine/models/hypercube_summary.py +91 -0
  121. gams/engine/models/hypercube_token.py +97 -0
  122. gams/engine/models/identity_provider.py +107 -0
  123. gams/engine/models/identity_provider_ldap.py +121 -0
  124. gams/engine/models/identity_provider_oauth2.py +146 -0
  125. gams/engine/models/identity_provider_oauth2_scope.py +89 -0
  126. gams/engine/models/identity_provider_oauth2_with_secret.py +152 -0
  127. gams/engine/models/identity_provider_oidc.py +133 -0
  128. gams/engine/models/identity_provider_oidc_with_secret.py +143 -0
  129. gams/engine/models/inex.py +91 -0
  130. gams/engine/models/invitation.py +136 -0
  131. gams/engine/models/invitation_quota.py +106 -0
  132. gams/engine/models/invitation_token.py +87 -0
  133. gams/engine/models/job.py +165 -0
  134. gams/engine/models/job_no_text_entry.py +138 -0
  135. gams/engine/models/job_no_text_entry_page.py +111 -0
  136. gams/engine/models/license.py +91 -0
  137. gams/engine/models/log_piece.py +96 -0
  138. gams/engine/models/message.py +87 -0
  139. gams/engine/models/message_and_token.py +99 -0
  140. gams/engine/models/message_with_webhook_id.py +89 -0
  141. gams/engine/models/model_auth_token.py +87 -0
  142. gams/engine/models/model_configuration.py +125 -0
  143. gams/engine/models/model_default_instance.py +99 -0
  144. gams/engine/models/model_default_user_instance.py +98 -0
  145. gams/engine/models/model_hypercube_job.py +106 -0
  146. gams/engine/models/model_hypercube_usage.py +130 -0
  147. gams/engine/models/model_instance_info.py +116 -0
  148. gams/engine/models/model_instance_info_full.py +123 -0
  149. gams/engine/models/model_instance_pool_info.py +112 -0
  150. gams/engine/models/model_job_labels.py +179 -0
  151. gams/engine/models/model_job_usage.py +133 -0
  152. gams/engine/models/model_pool_usage.py +124 -0
  153. gams/engine/models/model_usage.py +115 -0
  154. gams/engine/models/model_user.py +96 -0
  155. gams/engine/models/model_userinstance_info.py +119 -0
  156. gams/engine/models/model_userinstancepool_info.py +95 -0
  157. gams/engine/models/model_version.py +91 -0
  158. gams/engine/models/models.py +120 -0
  159. gams/engine/models/namespace.py +104 -0
  160. gams/engine/models/namespace_quota.py +96 -0
  161. gams/engine/models/namespace_with_permission.py +96 -0
  162. gams/engine/models/not_found.py +91 -0
  163. gams/engine/models/password_policy.py +97 -0
  164. gams/engine/models/perm_and_username.py +89 -0
  165. gams/engine/models/quota.py +117 -0
  166. gams/engine/models/quota_exceeded.py +97 -0
  167. gams/engine/models/status_code_meaning.py +89 -0
  168. gams/engine/models/stream_entry.py +89 -0
  169. gams/engine/models/system_wide_license.py +92 -0
  170. gams/engine/models/text_entries.py +87 -0
  171. gams/engine/models/text_entry.py +101 -0
  172. gams/engine/models/time_span.py +95 -0
  173. gams/engine/models/time_span_pool_worker.py +99 -0
  174. gams/engine/models/token_forward_error.py +87 -0
  175. gams/engine/models/user.py +127 -0
  176. gams/engine/models/user_group_member.py +96 -0
  177. gams/engine/models/user_groups.py +108 -0
  178. gams/engine/models/vapid_info.py +87 -0
  179. gams/engine/models/webhook.py +138 -0
  180. gams/engine/models/webhook_parameterized_event.py +99 -0
  181. gams/engine/py.typed +0 -0
  182. gams/engine/rest.py +258 -0
  183. gams/magic/__init__.py +32 -0
  184. gams/magic/gams_magic.py +142 -0
  185. gams/magic/interactive.py +402 -0
  186. gams/tools/__init__.py +30 -0
  187. gams/tools/errors.py +34 -0
  188. gams/tools/toolcollection/__init__.py +24 -0
  189. gams/tools/toolcollection/alg/__init__.py +24 -0
  190. gams/tools/toolcollection/alg/rank.py +51 -0
  191. gams/tools/toolcollection/data/__init__.py +24 -0
  192. gams/tools/toolcollection/data/csvread.py +444 -0
  193. gams/tools/toolcollection/data/csvwrite.py +311 -0
  194. gams/tools/toolcollection/data/exceldump.py +47 -0
  195. gams/tools/toolcollection/data/sqlitewrite.py +276 -0
  196. gams/tools/toolcollection/gdxservice/__init__.py +24 -0
  197. gams/tools/toolcollection/gdxservice/gdxencoding.py +104 -0
  198. gams/tools/toolcollection/gdxservice/gdxrename.py +94 -0
  199. gams/tools/toolcollection/linalg/__init__.py +24 -0
  200. gams/tools/toolcollection/linalg/cholesky.py +57 -0
  201. gams/tools/toolcollection/linalg/eigenvalue.py +56 -0
  202. gams/tools/toolcollection/linalg/eigenvector.py +58 -0
  203. gams/tools/toolcollection/linalg/invert.py +55 -0
  204. gams/tools/toolcollection/linalg/ols.py +138 -0
  205. gams/tools/toolcollection/tooltemplate.py +321 -0
  206. gams/tools/toolcollection/win32/__init__.py +24 -0
  207. gams/tools/toolcollection/win32/excelmerge.py +93 -0
  208. gams/tools/toolcollection/win32/exceltalk.py +76 -0
  209. gams/tools/toolcollection/win32/msappavail.py +49 -0
  210. gams/tools/toolcollection/win32/shellexecute.py +54 -0
  211. gams/tools/tools.py +116 -0
  212. gams/transfer/__init__.py +35 -0
  213. gams/transfer/_abcs/__init__.py +37 -0
  214. gams/transfer/_abcs/container_abcs.py +433 -0
  215. gams/transfer/_internals/__init__.py +63 -0
  216. gams/transfer/_internals/algorithms.py +436 -0
  217. gams/transfer/_internals/casepreservingdict.py +124 -0
  218. gams/transfer/_internals/constants.py +270 -0
  219. gams/transfer/_internals/domainviolation.py +103 -0
  220. gams/transfer/_internals/specialvalues.py +172 -0
  221. gams/transfer/containers/__init__.py +26 -0
  222. gams/transfer/containers/_container.py +1794 -0
  223. gams/transfer/containers/_io/__init__.py +28 -0
  224. gams/transfer/containers/_io/containers.py +164 -0
  225. gams/transfer/containers/_io/gdx.py +1029 -0
  226. gams/transfer/containers/_io/gmd.py +872 -0
  227. gams/transfer/containers/_mixins/__init__.py +26 -0
  228. gams/transfer/containers/_mixins/ccc.py +1274 -0
  229. gams/transfer/syms/__init__.py +33 -0
  230. gams/transfer/syms/_methods/__init__.py +24 -0
  231. gams/transfer/syms/_methods/tables.py +120 -0
  232. gams/transfer/syms/_methods/toDict.py +115 -0
  233. gams/transfer/syms/_methods/toList.py +83 -0
  234. gams/transfer/syms/_methods/toValue.py +60 -0
  235. gams/transfer/syms/_mixins/__init__.py +32 -0
  236. gams/transfer/syms/_mixins/equals.py +626 -0
  237. gams/transfer/syms/_mixins/generateRecords.py +499 -0
  238. gams/transfer/syms/_mixins/pivot.py +313 -0
  239. gams/transfer/syms/_mixins/pve.py +627 -0
  240. gams/transfer/syms/_mixins/sa.py +27 -0
  241. gams/transfer/syms/_mixins/sapve.py +27 -0
  242. gams/transfer/syms/_mixins/saua.py +27 -0
  243. gams/transfer/syms/_mixins/sauapve.py +199 -0
  244. gams/transfer/syms/_mixins/spve.py +1528 -0
  245. gams/transfer/syms/_mixins/ve.py +936 -0
  246. gams/transfer/syms/container_syms/__init__.py +31 -0
  247. gams/transfer/syms/container_syms/_alias.py +984 -0
  248. gams/transfer/syms/container_syms/_equation.py +333 -0
  249. gams/transfer/syms/container_syms/_parameter.py +973 -0
  250. gams/transfer/syms/container_syms/_set.py +604 -0
  251. gams/transfer/syms/container_syms/_universe_alias.py +461 -0
  252. gams/transfer/syms/container_syms/_variable.py +321 -0
  253. gamsapi-52.5.0.dist-info/METADATA +150 -0
  254. gamsapi-52.5.0.dist-info/RECORD +257 -0
  255. gamsapi-52.5.0.dist-info/WHEEL +5 -0
  256. gamsapi-52.5.0.dist-info/licenses/LICENSE +22 -0
  257. gamsapi-52.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1794 @@
1
+ #
2
+ # GAMS - General Algebraic Modeling System Python API
3
+ #
4
+ # Copyright (c) 2017-2026 GAMS Development Corp. <support@gams.com>
5
+ # Copyright (c) 2017-2026 GAMS Software GmbH <support@gams.com>
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+
26
+ import os
27
+ import pathlib
28
+ import pandas as pd
29
+ import numpy as np
30
+ import gams.core.numpy as gnp
31
+ from gams import GamsWorkspace, GamsDatabase
32
+ from gams.core import gmd
33
+ from gams.transfer._internals import (
34
+ CasePreservingDict,
35
+ DestinationType,
36
+ check_all_same,
37
+ )
38
+ import gams.transfer._abcs as abcs
39
+ from gams.transfer.containers._mixins import CCCMixin
40
+ import gams.transfer.containers._io as io
41
+ from gams.transfer.syms import (
42
+ Set,
43
+ Parameter,
44
+ Variable,
45
+ Equation,
46
+ Alias,
47
+ UniverseAlias,
48
+ )
49
+ from typing import Optional, Union, List, Dict, Any
50
+
51
+
52
+ def get_system_directory(system_directory: Union[None, str]) -> str:
53
+ """
54
+ Gets the GAMS system directory.
55
+
56
+ Parameters
57
+ ----------
58
+ system_directory : None | str
59
+ GAMS system directory. If None, itnwill try to get the default one.
60
+
61
+ Returns
62
+ -------
63
+ str
64
+ GAMS system directory.
65
+
66
+ Raises
67
+ ------
68
+ Exception
69
+ If no default system directory for a GAMS installation is found.
70
+ """
71
+ if system_directory is None:
72
+ try:
73
+ ws = GamsWorkspace()
74
+ sysdir = pathlib.Path(ws.system_directory)
75
+ except:
76
+ raise Exception(
77
+ "Could not find a GAMS installation, "
78
+ "must manually specify system_directory"
79
+ )
80
+ else:
81
+ sysdir = pathlib.Path(system_directory)
82
+
83
+ return os.fspath(sysdir.expanduser().resolve())
84
+
85
+
86
+ class Container(CCCMixin, abcs.ABCContainer):
87
+ """
88
+ A container is an object that holds all symbols and operates on them.
89
+
90
+ Parameters
91
+ ----------
92
+ load_from : GamsDatabase | PathLike | str | ABCContainer, optional
93
+ The data source to read from, by default None
94
+ system_directory : str, optional
95
+ Path to the directory that holds the GAMS installation, by default None
96
+
97
+ Examples
98
+ --------
99
+ >>> import gams.transfer as gt
100
+ >>> m = gt.Container()
101
+ >>> i = gt.Set(m, "i")
102
+
103
+ Attributes
104
+ ----------
105
+ data : dict
106
+ Dictionary that lists symbol names and symbol objects
107
+ modified : bool
108
+ Flag that identifies if the container has been modified
109
+ summary : dict
110
+ Returns a dict of only the metadata
111
+ system_directory : str
112
+ Returns the path to the system directory
113
+ """
114
+
115
+ def __init__(
116
+ self,
117
+ load_from: Optional[
118
+ Union["GamsDatabase", os.PathLike, str, "abcs.ABCContainer"]
119
+ ] = None,
120
+ system_directory: Optional[str] = None,
121
+ ) -> None:
122
+ # set up
123
+ self.system_directory = get_system_directory(system_directory)
124
+ self._gams2np = gnp.Gams2Numpy._bypass_workspace(self.system_directory)
125
+ self.data = CasePreservingDict()
126
+ self.modified = True
127
+ self._requires_state_check = True
128
+
129
+ # read
130
+ if load_from is not None:
131
+ self.read(load_from)
132
+
133
+ def __iter__(self):
134
+ return iter(self.data.items())
135
+
136
+ def __repr__(self):
137
+ return f"<GAMS Transfer Container ({hex(id(self))})>"
138
+
139
+ def __str__(self):
140
+ if len(self):
141
+ return f"<GAMS Transfer Container (w/ {len(self)} symbols)>"
142
+ else:
143
+ return f"<GAMS Transfer Container (empty)>"
144
+
145
+ def __getitem__(self, sym):
146
+ try:
147
+ return self.data[sym]
148
+ except KeyError:
149
+ raise KeyError(
150
+ f"Attempted retrieval of symbol `{sym}`, but `{sym}` does not"
151
+ " exist in the Container"
152
+ )
153
+
154
+ def __contains__(self, sym) -> bool:
155
+ if isinstance(sym, abcs.AnyContainerSymbol):
156
+ return hex(id(sym)) in [hex(id(i)) for i in self.data.values()]
157
+ elif isinstance(sym, str):
158
+ return sym in self.data
159
+ else:
160
+ return False
161
+
162
+ @property
163
+ def system_directory(self):
164
+ """
165
+ This property returns the path to the GAMS system directory.
166
+
167
+ Returns
168
+ -------
169
+ str
170
+ The path to the system directory.
171
+ """
172
+ return self._system_directory
173
+
174
+ @system_directory.setter
175
+ def system_directory(self, sysdir: Union[os.PathLike, str]) -> None:
176
+ """
177
+ Path to the system directory.
178
+
179
+ Parameters
180
+ ----------
181
+ sysdir : PathLike | str
182
+ The path to the system directory.
183
+ """
184
+ if not isinstance(sysdir, (os.PathLike, str)):
185
+ raise TypeError(
186
+ "'system_directory' expects type str or PathLike object, got"
187
+ f" {type(sysdir)}"
188
+ )
189
+
190
+ sysdir = pathlib.Path(sysdir).expanduser().resolve()
191
+
192
+ if not sysdir.is_dir():
193
+ raise Exception(
194
+ f"GAMS system_directory '{os.fspath(sysdir)}' is not a" " directory"
195
+ )
196
+
197
+ if not sysdir.exists():
198
+ raise Exception(
199
+ f"GAMS system_directory '{os.fspath(sysdir)}' does not exist, "
200
+ "check spelling or path specification"
201
+ )
202
+
203
+ if not pathlib.Path(sysdir, "optgams.def").is_file():
204
+ raise Exception(
205
+ f"GAMS system_directory '{os.fspath(sysdir)}' is not a valid"
206
+ " GAMS directory"
207
+ )
208
+
209
+ self._system_directory = os.fspath(sysdir)
210
+
211
+ @property
212
+ def summary(self) -> dict:
213
+ """
214
+ This property returns a summary of the container.
215
+
216
+ Returns
217
+ -------
218
+ dict
219
+ A summary including the container's system directory and its number of symbols.
220
+ """
221
+ return {
222
+ "system_directory": self.system_directory,
223
+ "number_symbols": len(self),
224
+ }
225
+
226
+ def _check_format_uels(self, symbols):
227
+ if not isinstance(symbols, (str, list, type(None))):
228
+ raise Exception("Argument 'symbols' must be type str, list or NoneType.")
229
+
230
+ if symbols is None:
231
+ symbols = self.listSymbols(is_valid=True)
232
+
233
+ if isinstance(symbols, str):
234
+ symbols = [symbols]
235
+
236
+ if any(not isinstance(i, str) for i in symbols):
237
+ raise Exception("Argument 'symbols' must only contain type str")
238
+
239
+ return symbols
240
+
241
+ def _formatUELs(self, method, symbols=None):
242
+ symbols = self._check_format_uels(symbols)
243
+
244
+ # loop through symbol objects
245
+ for symobj in self.getSymbols(symbols):
246
+ do_format = getattr(symobj, method)
247
+ do_format()
248
+
249
+ def lowerUELs(self, symbols: Optional[Union[str, List[str]]] = None) -> "Container":
250
+ """
251
+ This function converts the UELs of specific symbols to lowercase. If the 'symbols' parameter is set to None (default), all symbols in the container will be processed.
252
+
253
+ Parameters
254
+ ----------
255
+ symbols : str | List[str], optional
256
+ The symbol or list of symbols whose UELs you want to convert to lowercase. By default, it is set to None.
257
+
258
+ Returns
259
+ -------
260
+ Container
261
+ The updated container with some UELs converted to lowercase.
262
+ """
263
+ symbols = self._check_format_uels(symbols)
264
+
265
+ # loop through symbol objects
266
+ for symobj in self.getSymbols(symbols):
267
+ symobj.lowerUELs()
268
+
269
+ return self
270
+
271
+ def upperUELs(self, symbols: Optional[Union[str, List[str]]] = None) -> "Container":
272
+ """
273
+ This function converts the UELs of specific symbols to uppercase. If the 'symbols' parameter is set to None (default), all symbols in the container will be processed.
274
+
275
+ Parameters
276
+ ----------
277
+ symbols : str | List[str], optional
278
+ The symbol or list of symbols whose UELs you want to convert to uppercase. By default, it is set to None.
279
+
280
+ Returns
281
+ -------
282
+ Container
283
+ The updated container with some UELs converted to uppercase.
284
+ """
285
+ symbols = self._check_format_uels(symbols)
286
+
287
+ # loop through symbol objects
288
+ for symobj in self.getSymbols(symbols):
289
+ symobj.upperUELs()
290
+
291
+ return self
292
+
293
+ def lstripUELs(
294
+ self, symbols: Optional[Union[str, List[str]]] = None
295
+ ) -> "Container":
296
+ """
297
+ This function removes leading whitespaces from the UELs of specific symbols. If the 'symbols' parameter is set to None (default), all symbols in the container will undergo this operation.
298
+
299
+ Parameters
300
+ ----------
301
+ symbols : str | List[str], optional
302
+ The symbol or list of symbols whose UELs you wish to process. By default, it is set to None.
303
+
304
+ Returns
305
+ -------
306
+ Container
307
+ The updated container with leading whitespaces removed from the UELs of some symbols.
308
+ """
309
+ symbols = self._check_format_uels(symbols)
310
+
311
+ # loop through symbol objects
312
+ for symobj in self.getSymbols(symbols):
313
+ symobj.lstripUELs()
314
+
315
+ return self
316
+
317
+ def rstripUELs(
318
+ self, symbols: Optional[Union[str, List[str]]] = None
319
+ ) -> "Container":
320
+ """
321
+ This function removes trailing whitespaces from the UELs of specific symbols. If the 'symbols' parameter is set to None (default), all symbols in the container will undergo this operation.
322
+
323
+ Parameters
324
+ ----------
325
+ symbols : str | List[str], optional
326
+ The symbol or list of symbols whose UELs you wish to process. By default, it is set to None.
327
+
328
+ Returns
329
+ -------
330
+ Container
331
+ The updated container with trailing whitespaces removed from the UELs of some symbols.
332
+ """
333
+ symbols = self._check_format_uels(symbols)
334
+
335
+ # loop through symbol objects
336
+ for symobj in self.getSymbols(symbols):
337
+ symobj.rstripUELs()
338
+
339
+ return self
340
+
341
+ def stripUELs(self, symbols: Optional[Union[str, List[str]]] = None) -> "Container":
342
+ """
343
+ This function removes leading and trailing whitespaces from the UELs of specific symbols. If the 'symbols' parameter is set to None (default), all symbols in the container will undergo this operation.
344
+
345
+ Parameters
346
+ ----------
347
+ symbols : str | List[str], optional
348
+ The symbol or list of symbols whose UELs you wish to process. By default, it is set to None.
349
+
350
+ Returns
351
+ -------
352
+ Container
353
+ The updated container with leading and trailing whitespaces removed from the UELs of some symbols.
354
+ """
355
+ symbols = self._check_format_uels(symbols)
356
+
357
+ # loop through symbol objects
358
+ for symobj in self.getSymbols(symbols):
359
+ symobj.stripUELs()
360
+
361
+ return self
362
+
363
+ def capitalizeUELs(
364
+ self, symbols: Optional[Union[str, List[str]]] = None
365
+ ) -> "Container":
366
+ """
367
+ This function capitalizes the UELs of specific symbols. If the 'symbols' parameter is set to None (default), all symbols in the container will undergo this capitalization operation.
368
+
369
+ Parameters
370
+ ----------
371
+ symbols : str | List[str], optional
372
+ The symbol or list of symbols whose UELs you wish to capitalize. By default, it is set to None.
373
+
374
+ Returns
375
+ -------
376
+ Container
377
+ The updated container with UELs capitalized for some symbols.
378
+ """
379
+ symbols = self._check_format_uels(symbols)
380
+
381
+ # loop through symbol objects
382
+ for symobj in self.getSymbols(symbols):
383
+ symobj.capitalizeUELs()
384
+
385
+ return self
386
+
387
+ def casefoldUELs(
388
+ self, symbols: Optional[Union[str, List[str]]] = None
389
+ ) -> "Container":
390
+ """
391
+ This function performs case folding on the UELs of specific symbols, converting them to lowercase. If the 'symbols' parameter is set to None (default), all symbols in the container will undergo this case folding operation.
392
+
393
+ Parameters
394
+ ----------
395
+ symbols : str | List[str], optional
396
+ The symbol or list of symbols whose UELs you wish to convert to lowercase. By default, it is set to None.
397
+
398
+ Returns
399
+ -------
400
+ Container
401
+ The updated container with UELs converted to lowercase for some symbols.
402
+ """
403
+ symbols = self._check_format_uels(symbols)
404
+
405
+ # loop through symbol objects
406
+ for symobj in self.getSymbols(symbols):
407
+ symobj.casefoldUELs()
408
+
409
+ return self
410
+
411
+ def titleUELs(self, symbols: Optional[Union[str, List[str]]] = None) -> "Container":
412
+ """
413
+ This function capitalizes the UELs of specific symbols in title case. If the 'symbols' parameter is set to None (default), all symbols in the container will undergo this capitalization operation.
414
+
415
+ Parameters
416
+ ----------
417
+ symbols : str | List[str], optional
418
+ The symbol or list of symbols whose UELs you wish to capitalize in title case. By default, it is set to None.
419
+
420
+ Returns
421
+ -------
422
+ Container
423
+ The updated container with UELs capitalized in title case for some symbols.
424
+
425
+ """
426
+ symbols = self._check_format_uels(symbols)
427
+
428
+ # loop through symbol objects
429
+ for symobj in self.getSymbols(symbols):
430
+ symobj.titleUELs()
431
+
432
+ return self
433
+
434
+ def ljustUELs(
435
+ self,
436
+ length: int,
437
+ fill_character: Optional[str] = None,
438
+ symbols: Optional[Union[str, List[str]]] = None,
439
+ ) -> "Container":
440
+ """
441
+ This function left-justifies the UELs of specific symbols within the container, padding them with a specified fill character to reach the desired length.
442
+
443
+ Parameters
444
+ ----------
445
+ length : int
446
+ The target length to which UELs will be left-justified.
447
+
448
+ fill_character : str, optional
449
+ The character used for padding the UELs to the specified length. If not provided, it defaults to a whitespace.
450
+
451
+ symbols : str | List[str], optional
452
+ The symbol or list of symbols whose UELs you want to left-justify. By default, it is set to None, meaning all symbols in the container will undergo this left-justification.
453
+
454
+ Returns
455
+ -------
456
+ Container
457
+ The updated container with UELs left-justified for some symbols.
458
+ """
459
+ symbols = self._check_format_uels(symbols)
460
+
461
+ # loop through symbol objects
462
+ for symobj in self.getSymbols(symbols):
463
+ try:
464
+ symobj.ljustUELs(length, fill_character)
465
+ except Exception as err:
466
+ raise Exception(
467
+ "Could not successfully left justify (ljust) categories"
468
+ f" in `{symobj.name}`. Reason: {err}"
469
+ )
470
+
471
+ return self
472
+
473
+ def rjustUELs(
474
+ self,
475
+ length: int,
476
+ fill_character: Optional[str] = None,
477
+ symbols: Optional[Union[str, List[str]]] = None,
478
+ ) -> "Container":
479
+ """
480
+ This function right-justifies the UELs of specific symbols within the container, padding them with a specified fill character to reach the desired length.
481
+
482
+ Parameters
483
+ ----------
484
+ length : int
485
+ The target length to which UELs will be right-justified.
486
+
487
+ fill_character : str, optional
488
+ The character used for padding the UELs to the specified length. If not provided, it defaults to a whitespace.
489
+
490
+ symbols : str | List[str], optional
491
+ The symbol or list of symbols whose UELs you want to right-justify. By default, it is set to None, meaning all symbols in the container will undergo this right-justification.
492
+
493
+ Returns
494
+ -------
495
+ Container
496
+ The updated container with UELs right-justified for some symbols.
497
+ """
498
+ symbols = self._check_format_uels(symbols)
499
+
500
+ # loop through symbol objects
501
+ for symobj in self.getSymbols(symbols):
502
+ try:
503
+ symobj.rjustUELs(length, fill_character)
504
+ except Exception as err:
505
+ raise Exception(
506
+ "Could not successfully right justify (rjust) categories"
507
+ f" in `{symobj.name}`. Reason: {err}"
508
+ )
509
+ return self
510
+
511
+ def getUELs(
512
+ self,
513
+ symbols: Optional[Union[str, List[str]]] = None,
514
+ ignore_unused: bool = False,
515
+ unique_only: bool = False,
516
+ ) -> List[str]:
517
+ """
518
+ Retrieve the (UELs) associated with one or more symbols in the Container.
519
+
520
+ This method retrieves the (UELs) corresponding to the specified symbols in the Container.
521
+
522
+ Parameters
523
+ ----------
524
+ symbols : str | List[str], optional
525
+ An optional parameter specifying the symbol names for which to retrieve UELs, by default None.
526
+
527
+ ignore_unused : bool, optional
528
+ A flag to whether or not include unused symbols, by default False
529
+
530
+ unique_only : bool, optional
531
+ A flag to whether or not return only unique UELs, by default False
532
+
533
+ Returns
534
+ -------
535
+ List[str]
536
+ A list of (UELs) associated with the specified symbols.
537
+
538
+ Raises
539
+ ------
540
+ TypeError
541
+ If 'unique_only' is not of type bool.
542
+ TypeError
543
+ If 'symbols' is not of type str, iterable, or NoneType.
544
+ TypeError
545
+ If 'ignore_unused' is not of type bool.
546
+ Exception
547
+ If an element in 'symbols' is not of type str.
548
+ """
549
+
550
+ if not isinstance(unique_only, bool):
551
+ raise Exception("Argument 'unique_only' must be type bool.")
552
+
553
+ if not isinstance(symbols, (str, list, type(None))):
554
+ raise Exception("Argument 'symbols' must be type str, list or NoneType.")
555
+
556
+ if symbols is None:
557
+ symbols = self.listSymbols(is_valid=True)
558
+
559
+ if isinstance(symbols, str):
560
+ symbols = [symbols]
561
+
562
+ if not isinstance(ignore_unused, bool):
563
+ raise TypeError(f"Argument 'ignore_unused' must be type bool")
564
+
565
+ if any(not isinstance(i, str) for i in symbols):
566
+ raise Exception("Argument 'symbols' must only contain type str")
567
+
568
+ # loop through symbol objects and get UELs
569
+ uni = {}
570
+ for symobj in self.getSymbols(symbols):
571
+ if not isinstance(symobj, abcs.AnyContainerAlias):
572
+ if symobj.records is not None:
573
+ uni.update(
574
+ dict.fromkeys(symobj.getUELs(ignore_unused=ignore_unused))
575
+ )
576
+
577
+ if unique_only:
578
+ return list(CasePreservingDict().fromkeys(uni.keys()).keys())
579
+ else:
580
+ return list(uni.keys())
581
+
582
+ def renameUELs(
583
+ self,
584
+ uels: Dict[str, str],
585
+ symbols: Optional[List[str]] = None,
586
+ allow_merge: bool = False,
587
+ ) -> None:
588
+ """
589
+ Rename unique element labels (UELs) associated with symbols in the Container.
590
+
591
+ Parameters
592
+ ----------
593
+ uels : Dict[str, str]
594
+ A dictionary mapping old UELs to their new names. The keys are the old UELs that you want to rename,
595
+ and the values are the new names for those UELs
596
+
597
+ symbols : List[str]], optional
598
+ An optional parameter specifying the symbol names for which you want to rename UELs, by default None
599
+
600
+ allow_merge : bool, optional
601
+ A flag that allows merging UELs with existing UELs, by default False
602
+
603
+ Raises
604
+ ------
605
+ Exception
606
+ If the Container is currently invalid (not valid) because UELs (categories) cannot be accessed.
607
+ TypeError
608
+ If 'uels' is not of type dict.
609
+ TypeError
610
+ If 'symbols' is not of type list or NoneType.
611
+ TypeError
612
+ If an element in 'symbols' is not of type str.
613
+ """
614
+ if not self.isValid():
615
+ raise Exception(
616
+ "Container is currently invalid -- must be valid in order to"
617
+ " access UELs (categories)."
618
+ )
619
+
620
+ # ARG: uels
621
+ if not isinstance(uels, dict):
622
+ raise TypeError("Argument 'uels' must be type dict")
623
+
624
+ # ARG: symbols
625
+ if not isinstance(symbols, (list, type(None))):
626
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
627
+
628
+ if symbols is None:
629
+ symbols = list(self.data.keys())
630
+
631
+ if isinstance(symbols, list):
632
+ if any(not isinstance(i, str) for i in symbols):
633
+ raise TypeError("Argument 'symbols' must contain only type str")
634
+
635
+ for symobj in self.getSymbols(symbols):
636
+ if not isinstance(symobj, abcs.ABCUniverseAlias):
637
+ symobj.renameUELs(uels, allow_merge=allow_merge)
638
+
639
+ def removeUELs(
640
+ self,
641
+ uels: Optional[Union[str, List[str]]] = None,
642
+ symbols: Optional[List[str]] = None,
643
+ ) -> None:
644
+ """
645
+ Remove specific unique element labels (UELs) from one or more symbols in the Container.
646
+
647
+ Parameters
648
+ ----------
649
+ uels : str | List[str], optional
650
+ An optional parameter specifying the unique element labels (UELs) to be removed from the symbols, by default None
651
+
652
+ symbols : List[str], optional
653
+ An optional parameter specifying the symbols from which to remove the specified UELs, by default None
654
+
655
+ Raises
656
+ ------
657
+ TypeError
658
+ If 'uels' is not of type list, str, or NoneType.
659
+ TypeError
660
+ If an element in 'uels' is not of type str.
661
+ TypeError
662
+ If 'symbols' is not of type list or NoneType.
663
+ TypeError
664
+ If an element in 'symbols' is not of type str.
665
+
666
+ Exception
667
+ If the Container is currently invalid, as it must be valid to access UELs (categories).
668
+ """
669
+ if not self.isValid():
670
+ raise Exception(
671
+ "Container is currently invalid -- must be valid in order to"
672
+ " access UELs (categories)."
673
+ )
674
+
675
+ if not isinstance(uels, (list, str, type(None))):
676
+ raise TypeError("Argument 'uels' must be type list, str or NoneType")
677
+
678
+ if isinstance(uels, str):
679
+ uels = [uels]
680
+
681
+ if isinstance(uels, list):
682
+ if any(not isinstance(i, str) for i in uels):
683
+ raise TypeError("Argument 'uels' must contain only type str")
684
+
685
+ if not isinstance(symbols, (list, type(None))):
686
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
687
+
688
+ if symbols is None:
689
+ symbols = list(self.data.keys())
690
+
691
+ if isinstance(symbols, list):
692
+ if any(not isinstance(i, str) for i in symbols):
693
+ raise TypeError("Argument 'symbols' must contain only type str")
694
+
695
+ for symobj in self.getSymbols(symbols):
696
+ if not isinstance(symobj, abcs.ABCUniverseAlias):
697
+ symobj.removeUELs(uels)
698
+
699
+ def getDomainViolations(
700
+ self, symbols: Optional[Union[str, List[str]]] = None
701
+ ) -> Union[list, None]:
702
+ """
703
+ Get domain violations for one or more symbols in the Container.
704
+
705
+ Parameters
706
+ ----------
707
+ symbols : str | List[str], optional
708
+ An optional parameter specifying the symbols for which to retrieve domain violations, by default None
709
+
710
+ Returns
711
+ -------
712
+ list | None
713
+ A list of domain violations for the specified symbols, or None if there are no domain violations.
714
+
715
+ Raises
716
+ ------
717
+ TypeError
718
+ If 'symbols' is not of type list, str, or NoneType.
719
+ TypeError
720
+ If an element in 'symbols' is not of type str.
721
+ """
722
+ if not isinstance(symbols, (str, list, type(None))):
723
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
724
+
725
+ if symbols is None:
726
+ symbols = list(self.data.keys())
727
+
728
+ if isinstance(symbols, str):
729
+ symbols = [symbols]
730
+
731
+ if isinstance(symbols, list):
732
+ if any(not isinstance(i, str) for i in symbols):
733
+ raise TypeError("Argument 'symbols' must contain only type str")
734
+
735
+ dvs = []
736
+ for symobj in self.getSymbols(symbols):
737
+ violations = symobj.getDomainViolations()
738
+ if violations is not None:
739
+ dvs.extend(violations)
740
+
741
+ if len(dvs) != 0:
742
+ return dvs
743
+ else:
744
+ return None
745
+
746
+ def hasDomainViolations(
747
+ self, symbols: Optional[Union[str, List[str]]] = None
748
+ ) -> bool:
749
+ """
750
+ Check if domain violations exist for one or more symbols in the Container.
751
+
752
+ Parameters
753
+ ----------
754
+ symbols : str | List[str], optional
755
+ An optional parameter specifying the symbols to check for domain violations, by default None
756
+
757
+ Returns
758
+ -------
759
+ bool
760
+ True if domain violations exist for any of the specified symbols, False otherwise.
761
+
762
+ Raises
763
+ ------
764
+ TypeError
765
+ If 'symbols' is not of type list, str, or NoneType.
766
+ TypeError
767
+ If an element in 'symbols' is not of type str.
768
+ """
769
+ if not isinstance(symbols, (str, list, type(None))):
770
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
771
+
772
+ if symbols is None:
773
+ symbols = list(self.data.keys())
774
+
775
+ if isinstance(symbols, str):
776
+ symbols = [symbols]
777
+
778
+ if isinstance(symbols, list):
779
+ if any(not isinstance(i, str) for i in symbols):
780
+ raise TypeError("Argument 'symbols' must contain only type str")
781
+
782
+ for symobj in self.getSymbols(symbols):
783
+ if symobj.hasDomainViolations():
784
+ return True
785
+
786
+ return False
787
+
788
+ def countDomainViolations(
789
+ self, symbols: Optional[Union[str, List[str]]] = None
790
+ ) -> Dict[str, int]:
791
+ """
792
+ Count domain violations for one or more symbols in the Container.
793
+
794
+ Parameters
795
+ ----------
796
+ symbols : str | List[str], optional
797
+ An optional parameter specifying the symbols to count domain violations for, by default None
798
+
799
+ Returns
800
+ -------
801
+ Dict[str, int]
802
+ A dictionary where keys are symbol names with domain violations, and values are the corresponding counts of domain violations.
803
+
804
+ Raises
805
+ ------
806
+ TypeError
807
+ If 'symbols' is not of type list, str, or NoneType.
808
+ TypeError
809
+ If an element in 'symbols' is not of type str.
810
+ """
811
+ if not isinstance(symbols, (str, list, type(None))):
812
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
813
+
814
+ if symbols is None:
815
+ symbols = list(self.data.keys())
816
+
817
+ if isinstance(symbols, str):
818
+ symbols = [symbols]
819
+
820
+ if isinstance(symbols, list):
821
+ if any(not isinstance(i, str) for i in symbols):
822
+ raise TypeError("Argument 'symbols' must contain only type str")
823
+
824
+ dvs = {}
825
+
826
+ for symobj in self.getSymbols(symbols):
827
+ count = symobj.countDomainViolations()
828
+ if count != 0:
829
+ dvs.update({symobj.name: count})
830
+
831
+ return dvs
832
+
833
+ def dropDomainViolations(
834
+ self, symbols: Optional[Union[str, List[str]]] = None
835
+ ) -> None:
836
+ """
837
+ Drop domain violations for one or more symbols in the Container.
838
+
839
+ Parameters
840
+ ----------
841
+ symbols : str | List[str], optional
842
+ An optional parameter specifying the symbols to drop domain violations for, by default None
843
+
844
+ Raises
845
+ ------
846
+ TypeError
847
+ If 'symbols' is not of type list, str, or NoneType.
848
+ TypeError
849
+ If an element in 'symbols' is not of type str.
850
+ """
851
+ if not isinstance(symbols, (str, list, type(None))):
852
+ raise TypeError("Argument 'symbols' must be type str, list or NoneType")
853
+
854
+ if symbols is None:
855
+ symbols = list(self.countDomainViolations().keys())
856
+
857
+ if isinstance(symbols, str):
858
+ symbols = [symbols]
859
+
860
+ if isinstance(symbols, list):
861
+ if any(not isinstance(i, str) for i in symbols):
862
+ raise TypeError("Argument 'symbols' must contain only type str")
863
+
864
+ for symobj in self.getSymbols(symbols):
865
+ symobj.records.drop(index=symobj.findDomainViolations().index, inplace=True)
866
+
867
+ def countDuplicateRecords(
868
+ self, symbols: Optional[Union[str, List[str]]] = None
869
+ ) -> Dict[str, int]:
870
+ """
871
+ Count duplicate records for one or more symbols in the Container.
872
+
873
+ Parameters
874
+ ----------
875
+ symbols : str | List[str], optional
876
+ An optional parameter specifying the symbols to count duplicate records for, by default None
877
+
878
+ Returns
879
+ -------
880
+ Dict[str, int]
881
+ A dictionary where keys are symbol names with duplicate records, and values are the count of duplicate records for each symbol.
882
+
883
+ Raises
884
+ ------
885
+ TypeError
886
+ If 'symbols' is not of type list, str, or NoneType.
887
+ TypeError
888
+ If an element in 'symbols' is not of type str.
889
+ """
890
+ if not isinstance(symbols, (str, list, type(None))):
891
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
892
+
893
+ if symbols is None:
894
+ symbols = list(self.data.keys())
895
+
896
+ if isinstance(symbols, str):
897
+ symbols = [symbols]
898
+
899
+ if isinstance(symbols, list):
900
+ if any(not isinstance(i, str) for i in symbols):
901
+ raise TypeError("Argument 'symbols' must contain only type str")
902
+
903
+ dups = {}
904
+
905
+ for symobj in self.getSymbols(symbols):
906
+ count = symobj.countDuplicateRecords()
907
+ if count != 0:
908
+ dups.update({symobj.name: count})
909
+
910
+ return dups
911
+
912
+ def hasDuplicateRecords(
913
+ self, symbols: Optional[Union[str, List[str]]] = None
914
+ ) -> bool:
915
+ """
916
+ Check if the Container has any duplicate records for one or more symbols.
917
+
918
+ Parameters
919
+ ----------
920
+ symbols : str | List[str], optional
921
+ An optional parameter specifying the symbols to check for duplicate records, by default None
922
+
923
+ Returns
924
+ -------
925
+ bool
926
+ True if duplicate records are found for any of the specified symbols; False otherwise.
927
+
928
+ Raises
929
+ ------
930
+ TypeError
931
+ If 'symbols' is not of type list, str, or NoneType.
932
+ TypeError
933
+ If an element in 'symbols' is not of type str.
934
+ """
935
+ if not isinstance(symbols, (str, list, type(None))):
936
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
937
+
938
+ if symbols is None:
939
+ symbols = list(self.data.keys())
940
+
941
+ if isinstance(symbols, str):
942
+ symbols = [symbols]
943
+
944
+ if isinstance(symbols, list):
945
+ if any(not isinstance(i, str) for i in symbols):
946
+ raise TypeError("Argument 'symbols' must contain only type str")
947
+
948
+ for symobj in self.getSymbols(symbols):
949
+ if symobj.hasDuplicateRecords():
950
+ return True
951
+
952
+ return False
953
+
954
+ def dropDuplicateRecords(
955
+ self,
956
+ symbols: Optional[Union[str, List[str]]] = None,
957
+ keep: str = "first",
958
+ ) -> None:
959
+ """
960
+ Remove duplicate records from the specified symbols in the Container.
961
+
962
+ Parameters
963
+ ----------
964
+ symbols : str | List[str], optional
965
+ An optional parameter specifying the symbols from which to remove duplicate records, by default None
966
+
967
+ keep : str, optional
968
+ A string indicating which duplicate records to keep, by default "first"
969
+
970
+ Raises
971
+ ------
972
+ TypeError
973
+ If 'symbols' is not of type list, str, or NoneType.
974
+ TypeError
975
+ If an element in 'symbols' is not of type str.
976
+ """
977
+ if not isinstance(symbols, (str, list, type(None))):
978
+ raise TypeError("Argument 'symbols' must be type list or NoneType")
979
+
980
+ if symbols is None:
981
+ symbols = list(self.countDuplicateRecords().keys())
982
+
983
+ if isinstance(symbols, str):
984
+ symbols = [symbols]
985
+
986
+ if isinstance(symbols, list):
987
+ if any(not isinstance(i, str) for i in symbols):
988
+ raise TypeError("Argument 'symbols' must contain only type str")
989
+
990
+ for symobj in self.getSymbols(symbols):
991
+ symobj.records.drop(
992
+ index=symobj.findDuplicateRecords(keep).index, inplace=True
993
+ )
994
+
995
+ def _assert_valid_records(self, symbols=None):
996
+ if symbols is None:
997
+ symbols = self.listSymbols()
998
+
999
+ for symobj in self.getSymbols(symbols):
1000
+ if not isinstance(symobj, abcs.ABCUniverseAlias):
1001
+ symobj._assert_valid_records()
1002
+
1003
+ def _assert_is_valid(self, symbols):
1004
+ if self._requires_state_check:
1005
+ # make sure that all symbols have consistent naming
1006
+ for symname, symobj in zip(symbols, self.getSymbols(symbols)):
1007
+ if symname.casefold() != symobj.name.casefold():
1008
+ raise Exception(
1009
+ "Container data dict key is inconsistent with the"
1010
+ f" symbol object name (`{symname}` !="
1011
+ f" `{symobj.name}`). This inconsistency could have"
1012
+ " resulted from a symbol copy/deepcopy operation"
1013
+ " (i.e., `m[<new_symbol>] ="
1014
+ " copy.deepcopy(m[<existing_symbol>])`). Update"
1015
+ " symbol name with `<new_symbol>.name`."
1016
+ )
1017
+
1018
+ # make sure that all symbols reference the correct Container instance
1019
+ for symobj in self.getSymbols(symbols):
1020
+ if self != symobj.container:
1021
+ raise Exception(
1022
+ f"Symbol `{symobj.name}` has a broken Container"
1023
+ " reference. Symbol references Container at"
1024
+ f" {hex(id(symobj.container))} -- should be"
1025
+ f" referencing Container at {hex(id(self))}. This"
1026
+ " inconsistency could have resulted from a `deepcopy`"
1027
+ " of a symbol object (i.e., `new_container[<symbol>]"
1028
+ " = copy.deepcopy(old_container[<symbol>])`). Update"
1029
+ " symbol reference with `<symbol>.container ="
1030
+ " <new_container>`."
1031
+ )
1032
+
1033
+ if any(not symobj.isValid() for symobj in self.getSymbols(symbols)):
1034
+ raise Exception(
1035
+ "Container contains invalid symbols; invalid symbols can"
1036
+ " be found with the"
1037
+ " `<container>.listSymbols(is_valid=False)` method. Debug"
1038
+ " invalid symbol(s) by running"
1039
+ " `<symbol>.isValid(verbose=True, force=True)`` method on"
1040
+ " the symbol object."
1041
+ )
1042
+
1043
+ # check if there are graph cycles in the sets
1044
+ try:
1045
+ self._validSymbolOrder()
1046
+ except Exception as err:
1047
+ raise err
1048
+
1049
+ # if no exceptions, then turn self._requires_state_check 'off'
1050
+ self._requires_state_check = False
1051
+
1052
+ @property
1053
+ def modified(self) -> Union[bool, None]:
1054
+ return self._modified
1055
+
1056
+ @modified.setter
1057
+ def modified(self, modified: bool) -> None:
1058
+ if not isinstance(modified, bool):
1059
+ raise TypeError("Attribute 'modified' must be type bool")
1060
+
1061
+ self._modified = modified
1062
+
1063
+ if modified is False:
1064
+ for symname, symobj in self:
1065
+ symobj.modified = False
1066
+
1067
+ def _validSymbolOrder(self):
1068
+ ordered_symbols = []
1069
+ symbols_to_sort = [k for k, _ in self]
1070
+
1071
+ idx = 0
1072
+ while symbols_to_sort:
1073
+ sym = symbols_to_sort[idx]
1074
+
1075
+ # special 1D sets (universe domain & relaxed sets)
1076
+ if (
1077
+ isinstance(self.data[sym], abcs.ABCSet)
1078
+ and self.data[sym].dimension == 1
1079
+ and isinstance(self.data[sym].domain[0], str)
1080
+ ):
1081
+ ordered_symbols.append(self.data[sym].name)
1082
+ symbols_to_sort.pop(symbols_to_sort.index(sym))
1083
+ idx = 0
1084
+
1085
+ # everything else
1086
+ else:
1087
+ doi = []
1088
+ for i in self.data[sym].domain:
1089
+ if isinstance(i, str):
1090
+ doi.append(True)
1091
+ elif (
1092
+ isinstance(i, abcs.AnyContainerDomainSymbol)
1093
+ and i.name in ordered_symbols
1094
+ ):
1095
+ doi.append(True)
1096
+ else:
1097
+ doi.append(False)
1098
+
1099
+ if all(doi):
1100
+ ordered_symbols.append(sym)
1101
+ symbols_to_sort.pop(symbols_to_sort.index(sym))
1102
+ idx = 0
1103
+ else:
1104
+ idx += 1
1105
+
1106
+ if idx == len(symbols_to_sort) and symbols_to_sort != []:
1107
+ raise Exception(
1108
+ "Graph cycle detected among symbols:"
1109
+ f" {[i for i in symbols_to_sort if isinstance(self.data[i], abcs.ABCSet)]} --"
1110
+ " must resolve circular domain referencing"
1111
+ )
1112
+
1113
+ return ordered_symbols
1114
+
1115
+ def reorderSymbols(self) -> None:
1116
+ """
1117
+ Reorder symbols in order to avoid domain violations
1118
+ """
1119
+ self.data = CasePreservingDict(
1120
+ {k: self.data[k] for k in self._validSymbolOrder()}
1121
+ )
1122
+
1123
+ def _isValidSymbolOrder(self):
1124
+ valid_order = self._validSymbolOrder()
1125
+ current_order = [k for k, _ in self]
1126
+
1127
+ h = []
1128
+ for i in current_order:
1129
+ if isinstance(self.data[i], abcs.AnyContainerDomainSymbol):
1130
+ if current_order.index(i) <= valid_order.index(i):
1131
+ h.append(True)
1132
+ else:
1133
+ h.append(False)
1134
+ else:
1135
+ h.append(True)
1136
+
1137
+ if all(h):
1138
+ return True
1139
+ else:
1140
+ return False
1141
+
1142
+ def hasSymbols(self, symbols: Union[List[str], str]) -> Union[List[bool], bool]:
1143
+ """
1144
+ Checks if specific symbol names exist in a Container
1145
+
1146
+ Parameters
1147
+ ----------
1148
+ symbols : List[str] | str
1149
+ Symbols to check from the Container
1150
+
1151
+ Returns
1152
+ -------
1153
+ List[bool] | bool
1154
+ Flag to indicate whether a symbol exists or not
1155
+ """
1156
+ if not isinstance(symbols, (str, list)):
1157
+ raise TypeError("Argument 'symbols' must be type str or list")
1158
+
1159
+ if isinstance(symbols, list):
1160
+ return [sym in self for sym in symbols]
1161
+
1162
+ if isinstance(symbols, str):
1163
+ return symbols in self
1164
+
1165
+ def renameSymbol(self, old_name: str, new_name: str) -> None:
1166
+ """
1167
+ Rename a symbol in the Container
1168
+
1169
+ Parameters
1170
+ ----------
1171
+ old_name : str
1172
+ Old name of the symbol
1173
+ new_name : str
1174
+ New name of the symbol
1175
+ """
1176
+ if not isinstance(old_name, str):
1177
+ raise Exception("Argument 'old_name' must be type str")
1178
+
1179
+ if not isinstance(new_name, str):
1180
+ raise Exception("Argument 'new_name' must be type str")
1181
+
1182
+ if old_name.casefold() not in self:
1183
+ raise KeyError(f"Symbol `{old_name}` does not exist")
1184
+
1185
+ if old_name != new_name:
1186
+ self.data[old_name].name = new_name
1187
+ self._requires_state_check = True
1188
+
1189
+ def removeSymbols(self, symbols: Optional[Union[List[str], str]] = None) -> None:
1190
+ """
1191
+ Remove symbols from the Container. If symbols=None, it will remove all symbols.
1192
+
1193
+ Parameters
1194
+ ----------
1195
+ symbols : List[str] | str, optional
1196
+ Symbols to remove from the Container, also sets the symbols container to None. If symbols=None, will remove all symbols.
1197
+ """
1198
+ if symbols is None:
1199
+ self.data = CasePreservingDict()
1200
+ return
1201
+
1202
+ # ARG: symbols
1203
+ if not isinstance(symbols, (str, list)):
1204
+ raise Exception("Argument 'symbols' must be type str or list")
1205
+
1206
+ if isinstance(symbols, str):
1207
+ symbols = [symbols]
1208
+
1209
+ if not all([isinstance(i, str) for i in symbols]):
1210
+ raise Exception("Argument 'symbols' must contain only type str")
1211
+
1212
+ # test if all symbols are in the Container
1213
+ for i in symbols:
1214
+ if i not in self:
1215
+ raise ValueError(
1216
+ f"User specified to remove symbol `{i}`, "
1217
+ "but it does not exist in the container."
1218
+ )
1219
+
1220
+ # find sets or aliases that are being removed
1221
+ set_or_alias = []
1222
+ [
1223
+ set_or_alias.append(symobj)
1224
+ for symobj in self.getSymbols(symbols)
1225
+ if isinstance(symobj, abcs.AnyContainerDomainSymbol)
1226
+ ]
1227
+
1228
+ # remove symbols
1229
+ for symobj in self.getSymbols(symbols):
1230
+ # mark symbol container as None and reset state check flag
1231
+ symobj._container = None
1232
+ symobj._requires_state_check = True
1233
+
1234
+ # remove the symbol
1235
+ self.data.pop(symobj.name)
1236
+
1237
+ # remove alias symbols if parent is removed
1238
+ symbols = list(self)
1239
+ for symname, symobj in symbols:
1240
+ if isinstance(symobj, abcs.ABCAlias):
1241
+ if symobj.alias_with in set_or_alias:
1242
+ self.removeSymbols(symobj.name)
1243
+
1244
+ # search through all symbols and remove domain references
1245
+ for symname, symobj in self:
1246
+ # find new domain
1247
+ new_domain = []
1248
+ for n, symdom in enumerate(symobj.domain):
1249
+ if symdom in set_or_alias:
1250
+ new_domain.append("*")
1251
+ else:
1252
+ new_domain.append(symdom)
1253
+
1254
+ # set new (relaxed) domain
1255
+ symobj.domain = new_domain
1256
+
1257
+ # reset flags
1258
+ if set_or_alias:
1259
+ for symname, symobj in self:
1260
+ symobj._requires_state_check = True
1261
+ symobj.modified = True
1262
+
1263
+ # reset state check flag for the container
1264
+ self._requires_state_check = True
1265
+
1266
+ def addSet(
1267
+ self,
1268
+ name: str,
1269
+ domain: Optional[List[Union["Set", str]]] = None,
1270
+ is_singleton: bool = False,
1271
+ records: Optional[Any] = None,
1272
+ domain_forwarding: bool = False,
1273
+ description: str = "",
1274
+ uels_on_axes: bool = False,
1275
+ ) -> "Set":
1276
+ """
1277
+ Add a Set symbol to the Container or update an existing Set symbol.
1278
+
1279
+ Parameters
1280
+ ----------
1281
+ name : str
1282
+ The name of the Set symbol to be added or updated.
1283
+
1284
+ domain : List[Set | str], optional
1285
+ An optional parameter specifying the domain (index set) of the Set symbol, by default None
1286
+
1287
+ is_singleton : bool, optional
1288
+ A boolean indicating whether the Set is a singleton set, by default False
1289
+
1290
+ records : Any, optional
1291
+ An optional parameter specifying the records (elements) of the Set symbol.
1292
+
1293
+ domain_forwarding : bool, optional
1294
+ A boolean indicating whether domain forwarding is enabled for the Set symbol, by default False
1295
+
1296
+ description : str, optional
1297
+ A description or comment for the Set symbol.
1298
+
1299
+ uels_on_axes : bool, optional
1300
+ A boolean indicating whether unique element lists (UELs) are used as axis values in the symbol, by default False
1301
+
1302
+ Returns
1303
+ -------
1304
+ Set
1305
+ The Set object that was added or updated in the Container.
1306
+
1307
+ Raises
1308
+ ------
1309
+ ValueError
1310
+ If an attempt is made to update an existing Set symbol with conflicting properties.
1311
+ TypeError
1312
+ If 'domain' is not of type list, Set, or str.
1313
+ TypeError
1314
+ If 'is_singleton' is not of type bool.
1315
+ TypeError
1316
+ If 'domain_forwarding' is not of type bool.
1317
+ TypeError
1318
+ If 'uels_on_axes' is not of type bool.
1319
+
1320
+ Examples
1321
+ --------
1322
+ >>> import gams.transfer as gt
1323
+ >>> m = gt.Container()
1324
+ >>> i = m.addSet("i", records=['i1','i2'])
1325
+ >>> print(i.getUELs())
1326
+ ['i1', 'i2']
1327
+
1328
+ """
1329
+ # allows overwriting
1330
+ return Set(
1331
+ self,
1332
+ name,
1333
+ domain,
1334
+ is_singleton,
1335
+ records,
1336
+ domain_forwarding,
1337
+ description,
1338
+ uels_on_axes,
1339
+ )
1340
+
1341
+ def addParameter(
1342
+ self,
1343
+ name: str,
1344
+ domain: Optional[List[Union[str, "Set"]]] = None,
1345
+ records: Optional[Any] = None,
1346
+ domain_forwarding: bool = False,
1347
+ description: str = "",
1348
+ uels_on_axes: bool = False,
1349
+ ) -> "Parameter":
1350
+ """
1351
+ Add a Parameter symbol to the Container or update an existing Parameter symbol.
1352
+
1353
+ Parameters
1354
+ ----------
1355
+ name : str
1356
+ The name of the Parameter symbol to be added or updated.
1357
+
1358
+ domain : List[Set | str], optional
1359
+ An optional parameter specifying the domain (index set) of the Parameter symbol, by default None
1360
+
1361
+ records : Any, optional
1362
+ An optional parameter specifying the records (elements) of the Parameter symbol.
1363
+
1364
+ domain_forwarding : bool, optional
1365
+ A boolean indicating whether domain forwarding is enabled for the Parameter symbol, by default False
1366
+
1367
+ description : str, optional
1368
+ A description or comment for the Parameter symbol.
1369
+
1370
+ uels_on_axes : bool, optional
1371
+ A boolean indicating whether unique element lists (UELs) are used as axis values in the symbol, by default False
1372
+
1373
+ Returns
1374
+ -------
1375
+ Parameter
1376
+ The Parameter symbol that was added or updated in the Container.
1377
+
1378
+ Raises
1379
+ ------
1380
+ ValueError
1381
+ If an attempt is made to update an existing Parameter symbol with conflicting properties.
1382
+ TypeError
1383
+ If 'domain' is not of type list, Set, or str.
1384
+ TypeError
1385
+ If 'is_singleton' is not of type bool.
1386
+ TypeError
1387
+ If 'domain_forwarding' is not of type bool.
1388
+ TypeError
1389
+ If 'uels_on_axes' is not of type bool.
1390
+
1391
+ Examples
1392
+ --------
1393
+ >>> import gams.transfer as gt
1394
+ >>> m = gt.Container()
1395
+ >>> a = m.addParameter("a", records=5)
1396
+ >>> print(a.toValue())
1397
+ 5.0
1398
+
1399
+ """
1400
+ # allows overwriting
1401
+ return Parameter(
1402
+ self,
1403
+ name,
1404
+ domain,
1405
+ records,
1406
+ domain_forwarding,
1407
+ description,
1408
+ uels_on_axes,
1409
+ )
1410
+
1411
+ def addVariable(
1412
+ self,
1413
+ name: str,
1414
+ type: str = "free",
1415
+ domain: Optional[List[Union[str, "Set"]]] = None,
1416
+ records: Optional[Any] = None,
1417
+ domain_forwarding: bool = False,
1418
+ description: str = "",
1419
+ uels_on_axes: bool = False,
1420
+ ) -> "Variable":
1421
+ """
1422
+ Add a Variable symbol to the Container or update an existing Variable symbol.
1423
+
1424
+ Parameters
1425
+ ----------
1426
+ name : str
1427
+ The name of the Variable symbol to be added or updated.
1428
+
1429
+ type : str, optional
1430
+ The type of the Variable symbol, by default "free"
1431
+
1432
+ domain : List[Set | str], optional
1433
+ An optional parameter specifying the domain (index set) of the Variable symbol, by default None
1434
+
1435
+ records : Any, optional
1436
+ An optional parameter specifying the records (elements) of the Variable symbol.
1437
+
1438
+ domain_forwarding : bool, optional
1439
+ A boolean indicating whether domain forwarding is enabled for the Variable symbol, by default False
1440
+
1441
+ description : str, optional
1442
+ A description or comment for the Variable symbol.
1443
+
1444
+ uels_on_axes : bool, optional
1445
+ A boolean indicating whether unique element lists (UELs) are used as axis values in the symbol, by default False
1446
+
1447
+ Returns
1448
+ -------
1449
+ Variable
1450
+ The Variable object that was added or updated in the Container.
1451
+
1452
+ Raises
1453
+ ------
1454
+ ValueError
1455
+ If an attempt is made to update an existing Variable symbol with conflicting properties.
1456
+ TypeError
1457
+ If 'domain' is not of type list, Set, or str.
1458
+ TypeError
1459
+ If 'is_singleton' is not of type bool.
1460
+ TypeError
1461
+ If 'domain_forwarding' is not of type bool.
1462
+ TypeError
1463
+ If 'uels_on_axes' is not of type bool.
1464
+
1465
+ Examples
1466
+ --------
1467
+ >>> import gams.transfer as gt
1468
+ >>> m = gt.Container()
1469
+ >>> i = m.addSet("i", records=['i1','i2'])
1470
+ >>> v = m.addVariable("a", domain=[i])
1471
+ >>> print(v.domain[0] == m["i"])
1472
+ True
1473
+
1474
+ """
1475
+ # allows overwriting
1476
+ return Variable(
1477
+ self,
1478
+ name,
1479
+ type,
1480
+ domain,
1481
+ records,
1482
+ domain_forwarding,
1483
+ description,
1484
+ uels_on_axes,
1485
+ )
1486
+
1487
+ def addEquation(
1488
+ self,
1489
+ name: str,
1490
+ type: str = "regular",
1491
+ domain: Optional[List[Union["Set", str]]] = None,
1492
+ records: Optional[Any] = None,
1493
+ domain_forwarding: bool = False,
1494
+ description: str = "",
1495
+ uels_on_axes: bool = False,
1496
+ ) -> "Equation":
1497
+ """
1498
+ Add an Equation symbol to the Container or update an existing Equation symbol.
1499
+
1500
+ Parameters
1501
+ ----------
1502
+ name : str
1503
+ The name of the Equation symbol to be added or updated.
1504
+
1505
+ type : str, optional
1506
+ The type of the Equation symbol, by default "free"
1507
+
1508
+ domain : List[Set | str], optional
1509
+ An optional parameter specifying the domain (index set) of the Equation symbol, by default None
1510
+
1511
+ records : Any, optional
1512
+ An optional parameter specifying the records (elements) of the Equation symbol.
1513
+
1514
+ domain_forwarding : bool, optional
1515
+ A boolean indicating whether domain forwarding is enabled for the Equation symbol, by default False
1516
+
1517
+ description : str, optional
1518
+ A description or comment for the Equation symbol.
1519
+
1520
+ uels_on_axes : bool, optional
1521
+ A boolean indicating whether unique element lists (UELs) are used as axis values in the symbol, by default False
1522
+
1523
+ Returns
1524
+ -------
1525
+ Equation
1526
+ The Equation object that was added or updated in the Container.
1527
+
1528
+
1529
+ """
1530
+ # allows overwriting
1531
+ return Equation(
1532
+ self,
1533
+ name,
1534
+ type,
1535
+ domain,
1536
+ records,
1537
+ domain_forwarding,
1538
+ description,
1539
+ uels_on_axes,
1540
+ )
1541
+
1542
+ def addAlias(self, name: str, alias_with: "Set") -> "Alias":
1543
+ """
1544
+ Adds an Alias to the Container.
1545
+
1546
+ Parameters
1547
+ ----------
1548
+ name : str
1549
+ The name of the Alias symbol
1550
+ alias_with : Set
1551
+ The Set to alias
1552
+
1553
+ Returns
1554
+ -------
1555
+ Alias
1556
+ The Alias object that was added
1557
+
1558
+ Examples
1559
+ --------
1560
+ >>> import gams.transfer as gt
1561
+ >>> m = gt.Container()
1562
+ >>> i = m.addSet("i", records=['i1','i2'])
1563
+ >>> j = m.addAlias("j", i)
1564
+ >>> print(j.getUELs())
1565
+ ['i1', 'i2']
1566
+
1567
+ """
1568
+ # allows overwriting
1569
+ return Alias(self, name, alias_with)
1570
+
1571
+ def addUniverseAlias(self, name: str) -> "UniverseAlias":
1572
+ """
1573
+ Add a Universe Alias to the Container
1574
+
1575
+ Parameters
1576
+ ----------
1577
+ name : str
1578
+ The name of the Universe Alias symbol
1579
+
1580
+ Returns
1581
+ -------
1582
+ UniverseAlias
1583
+ The Universe Alias object that was added
1584
+
1585
+ Examples
1586
+ --------
1587
+ >>> import gams.transfer as gt
1588
+ >>> m = gt.Container()
1589
+ >>> i = m.addSet("i", records=['i1','i2'])
1590
+ >>> j = m.addSet("j", records=['j1','j2','j3'])
1591
+ >>> ij = m.addUniverseAlias("ij")
1592
+ >>> print(ij.getUELs())
1593
+ ['i1', 'i2', 'j1', 'j2', 'j3']
1594
+
1595
+ """
1596
+ # allows overwriting
1597
+ return UniverseAlias(self, name)
1598
+
1599
+ def _gdx_read(self, load_from, symbols, records, mode, encoding):
1600
+ return io.gdx.container_read(self, load_from, symbols, records, mode, encoding)
1601
+
1602
+ def _gdx_write(self, write_to, symbols, uel_priority, compress, mode, eps_to_zero):
1603
+ return io.gdx.container_write(
1604
+ self, write_to, symbols, uel_priority, compress, mode, eps_to_zero
1605
+ )
1606
+
1607
+ def _gmd_read(self, load_from, symbols, records, mode, encoding):
1608
+ return io.gmd.container_read(self, load_from, symbols, records, mode, encoding)
1609
+
1610
+ def _gmd_write(
1611
+ self, write_to, symbols, uel_priority, merge_symbols, mode, eps_to_zero
1612
+ ):
1613
+ return io.gmd.container_write(
1614
+ self,
1615
+ write_to,
1616
+ symbols,
1617
+ uel_priority,
1618
+ merge_symbols,
1619
+ mode,
1620
+ eps_to_zero,
1621
+ )
1622
+
1623
+ def _container_read(self, load_from, symbols, records):
1624
+ return io.containers.read(self, load_from, symbols, records)
1625
+
1626
+ def write(
1627
+ self,
1628
+ write_to: Union["GamsDatabase", os.PathLike, str],
1629
+ symbols: Optional[Union[List[str], str]] = None,
1630
+ compress: bool = False,
1631
+ uel_priority: Optional[Union[List[str], str]] = None,
1632
+ merge_symbols: Optional[Union[List[str], str]] = None,
1633
+ mode: Optional[str] = None,
1634
+ eps_to_zero: bool = True,
1635
+ ) -> None:
1636
+ """
1637
+ Write data from the Container to a GDX or GMD file.
1638
+
1639
+ Parameters
1640
+ ----------
1641
+ write_to : GamsDatabase | PathLike | str
1642
+ The destination where the data should be written.
1643
+
1644
+ symbols : List[str] | str, optional
1645
+ An optional parameter specifying the symbols to be written, by default None
1646
+
1647
+ compress : bool, optional
1648
+ A boolean indicating whether to compress the GDX file, by default False
1649
+
1650
+ uel_priority : List[str] | str, optional
1651
+ An optional parameter specifying the UEL (Unique Element List) priority, by default None
1652
+
1653
+ merge_symbols : List[str] | str, optional
1654
+ An optional parameter specifying symbols to merge when writing to a GMD object, by default None
1655
+
1656
+ mode : Optional[str], optional
1657
+ An optional parameter specifying the writing mode, by default None
1658
+
1659
+ Raises
1660
+ ------
1661
+ TypeError
1662
+ If 'symbols' is not of type str, list, or NoneType.
1663
+ TypeError
1664
+ If 'compress' is not of type bool.
1665
+ TypeError
1666
+ If 'uel_priority' is not of type str, list, or NoneType.
1667
+ TypeError
1668
+ If 'merge_symbols' is not of type str, list, or NoneType.
1669
+ TypeError
1670
+ If 'mode' is not of type str or NoneType.
1671
+ ValueError
1672
+ If 'write_to' is not a valid destination (GamsDatabase, GDX file path, or GMD object).
1673
+ ValueError
1674
+ If 'mode' is not one of the valid values ("string", "category", or None).
1675
+ """
1676
+ # check symbols argument
1677
+ if not isinstance(symbols, (str, list, type(None))):
1678
+ raise TypeError("Argument 'symbols' must be type str, list or NoneType")
1679
+
1680
+ if isinstance(symbols, str):
1681
+ symbols = [symbols]
1682
+
1683
+ if symbols is not None:
1684
+ if any(not isinstance(i, str) for i in symbols):
1685
+ raise TypeError("Argument 'symbols' must contain only type str")
1686
+
1687
+ # check compress argument
1688
+ if not isinstance(compress, bool):
1689
+ raise TypeError(
1690
+ "Argument 'compress' must be of type bool; default "
1691
+ "False (no compression); ignored if writing to a GMD object."
1692
+ )
1693
+
1694
+ # check eps_to_zero
1695
+ if not isinstance(eps_to_zero, bool):
1696
+ raise TypeError("Argument 'eps_to_zero' must be type bool")
1697
+
1698
+ # check uel_priority argument
1699
+ if not isinstance(uel_priority, (str, list, type(None))):
1700
+ raise TypeError(
1701
+ "Argument 'uel_priority' must be type str, list or NoneType"
1702
+ )
1703
+
1704
+ if isinstance(uel_priority, str):
1705
+ uel_priority = [uel_priority]
1706
+
1707
+ # check merge_symbols argument
1708
+ if not isinstance(merge_symbols, (str, list, type(None))):
1709
+ raise TypeError(
1710
+ "Argument 'merge_symbols' must be type str, list or NoneType"
1711
+ )
1712
+
1713
+ if isinstance(merge_symbols, str):
1714
+ merge_symbols = [merge_symbols]
1715
+
1716
+ if merge_symbols is None:
1717
+ merge_symbols = []
1718
+
1719
+ if isinstance(merge_symbols, list):
1720
+ if any(not isinstance(i, str) for i in merge_symbols):
1721
+ raise TypeError("Argument 'merge_symbols' must contain only type str")
1722
+
1723
+ # check mode argument
1724
+ if not isinstance(mode, (str, type(None))):
1725
+ raise TypeError("Argument 'mode' must be type str or NoneType")
1726
+
1727
+ if mode is None:
1728
+ mode = "category"
1729
+ else:
1730
+ mode = mode.casefold()
1731
+ if mode not in ["string", "category"]:
1732
+ raise ValueError(
1733
+ "Argument 'mode' must be `string`, `category` or `None`"
1734
+ )
1735
+
1736
+ #
1737
+ # figure out data write_to type
1738
+ if isinstance(write_to, GamsDatabase):
1739
+ dest = DestinationType.GMD
1740
+ write_to = write_to._gmd
1741
+
1742
+ elif isinstance(write_to, (os.PathLike, str)):
1743
+ fpath = pathlib.Path(write_to)
1744
+
1745
+ if not os.fspath(fpath.expanduser().resolve()).casefold().endswith(".gdx"):
1746
+ raise Exception(
1747
+ "Unexpected file type passed to 'write_to' argument "
1748
+ "-- expected file extension '.gdx'"
1749
+ )
1750
+
1751
+ dest = DestinationType.GDX
1752
+ write_to = os.fspath(fpath.expanduser().resolve())
1753
+
1754
+ else:
1755
+ # try GMD, if not, then mark as unknown
1756
+ try:
1757
+ ret = gmd.gmdInfo(write_to, gmd.GMD_NRSYMBOLSWITHALIAS)
1758
+ assert ret[0] == 1
1759
+ dest = DestinationType.GMD
1760
+ except:
1761
+ dest = DestinationType.UNKNOWN
1762
+
1763
+ # throw error if user wants to merge symbols but write_to is not a GMD object
1764
+ if dest is DestinationType.GDX and len(merge_symbols) != 0:
1765
+ raise Exception(
1766
+ "Symbol merge operations are only possible when writing to a"
1767
+ " valid GMD object."
1768
+ )
1769
+
1770
+ #
1771
+ # test and write to different destinations
1772
+ #
1773
+ if dest is DestinationType.UNKNOWN:
1774
+ raise TypeError(
1775
+ "Argument 'write_to' expects "
1776
+ "type str or Pathlike object (i.e., a path to a GDX file) "
1777
+ "or a valid gmdHandle (or GamsDatabase instance) "
1778
+ f"User passed: '{type(write_to)}'."
1779
+ )
1780
+
1781
+ if dest is DestinationType.GDX:
1782
+ self._gdx_write(
1783
+ write_to, symbols, uel_priority, compress, mode, eps_to_zero
1784
+ )
1785
+
1786
+ if dest is DestinationType.GMD:
1787
+ self._gmd_write(
1788
+ write_to,
1789
+ symbols,
1790
+ uel_priority,
1791
+ merge_symbols,
1792
+ mode,
1793
+ eps_to_zero,
1794
+ )