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.
- gams/__init__.py +27 -0
- gams/_version.py +1 -0
- gams/connect/__init__.py +28 -0
- gams/connect/agents/__init__.py +24 -0
- gams/connect/agents/_excel/__init__.py +32 -0
- gams/connect/agents/_excel/excelagent.py +312 -0
- gams/connect/agents/_excel/workbook.py +155 -0
- gams/connect/agents/_sqlconnectors/__init__.py +42 -0
- gams/connect/agents/_sqlconnectors/_accesshandler.py +211 -0
- gams/connect/agents/_sqlconnectors/_databasehandler.py +250 -0
- gams/connect/agents/_sqlconnectors/_mysqlhandler.py +168 -0
- gams/connect/agents/_sqlconnectors/_postgreshandler.py +131 -0
- gams/connect/agents/_sqlconnectors/_pyodbchandler.py +112 -0
- gams/connect/agents/_sqlconnectors/_sqlalchemyhandler.py +74 -0
- gams/connect/agents/_sqlconnectors/_sqlitehandler.py +262 -0
- gams/connect/agents/_sqlconnectors/_sqlserverhandler.py +179 -0
- gams/connect/agents/concatenate.py +440 -0
- gams/connect/agents/connectagent.py +743 -0
- gams/connect/agents/csvreader.py +675 -0
- gams/connect/agents/csvwriter.py +151 -0
- gams/connect/agents/domainwriter.py +143 -0
- gams/connect/agents/excelreader.py +756 -0
- gams/connect/agents/excelwriter.py +467 -0
- gams/connect/agents/filter.py +223 -0
- gams/connect/agents/gamsreader.py +112 -0
- gams/connect/agents/gamswriter.py +239 -0
- gams/connect/agents/gdxreader.py +109 -0
- gams/connect/agents/gdxwriter.py +146 -0
- gams/connect/agents/labelmanipulator.py +303 -0
- gams/connect/agents/projection.py +539 -0
- gams/connect/agents/pythoncode.py +71 -0
- gams/connect/agents/rawcsvreader.py +248 -0
- gams/connect/agents/rawexcelreader.py +312 -0
- gams/connect/agents/schema/CSVReader.yaml +92 -0
- gams/connect/agents/schema/CSVWriter.yaml +44 -0
- gams/connect/agents/schema/Concatenate.yaml +52 -0
- gams/connect/agents/schema/DomainWriter.yaml +25 -0
- gams/connect/agents/schema/ExcelReader.yaml +121 -0
- gams/connect/agents/schema/ExcelWriter.yaml +78 -0
- gams/connect/agents/schema/Filter.yaml +74 -0
- gams/connect/agents/schema/GAMSReader.yaml +20 -0
- gams/connect/agents/schema/GAMSWriter.yaml +47 -0
- gams/connect/agents/schema/GDXReader.yaml +23 -0
- gams/connect/agents/schema/GDXWriter.yaml +32 -0
- gams/connect/agents/schema/LabelManipulator.yaml +99 -0
- gams/connect/agents/schema/Projection.yaml +24 -0
- gams/connect/agents/schema/PythonCode.yaml +6 -0
- gams/connect/agents/schema/RawCSVReader.yaml +34 -0
- gams/connect/agents/schema/RawExcelReader.yaml +42 -0
- gams/connect/agents/schema/SQLReader.yaml +75 -0
- gams/connect/agents/schema/SQLWriter.yaml +103 -0
- gams/connect/agents/sqlreader.py +301 -0
- gams/connect/agents/sqlwriter.py +276 -0
- gams/connect/connectdatabase.py +275 -0
- gams/connect/connectvalidator.py +93 -0
- gams/connect/errors.py +34 -0
- gams/control/__init__.py +136 -0
- gams/control/database.py +2231 -0
- gams/control/execution.py +1900 -0
- gams/control/options.py +2792 -0
- gams/control/workspace.py +1198 -0
- gams/core/__init__.py +24 -0
- gams/core/cfg/__init__.py +26 -0
- gams/core/cfg/_cfgmcc.cp312-win_amd64.pyd +0 -0
- gams/core/cfg/cfgmcc.py +519 -0
- gams/core/dct/__init__.py +26 -0
- gams/core/dct/_dctmcc.cp312-win_amd64.pyd +0 -0
- gams/core/dct/dctmcc.py +574 -0
- gams/core/embedded/__init__.py +26 -0
- gams/core/embedded/gamsemb.py +1024 -0
- gams/core/emp/__init__.py +24 -0
- gams/core/emp/emplexer.py +89 -0
- gams/core/emp/empyacc.py +281 -0
- gams/core/gdx/__init__.py +26 -0
- gams/core/gdx/_gdxcc.cp312-win_amd64.pyd +0 -0
- gams/core/gdx/gdxcc.py +866 -0
- gams/core/gev/__init__.py +26 -0
- gams/core/gev/_gevmcc.cp312-win_amd64.pyd +0 -0
- gams/core/gev/gevmcc.py +855 -0
- gams/core/gmd/__init__.py +26 -0
- gams/core/gmd/_gmdcc.cp312-win_amd64.pyd +0 -0
- gams/core/gmd/gmdcc.py +917 -0
- gams/core/gmo/__init__.py +26 -0
- gams/core/gmo/_gmomcc.cp312-win_amd64.pyd +0 -0
- gams/core/gmo/gmomcc.py +2046 -0
- gams/core/idx/__init__.py +26 -0
- gams/core/idx/_idxcc.cp312-win_amd64.pyd +0 -0
- gams/core/idx/idxcc.py +510 -0
- gams/core/numpy/__init__.py +29 -0
- gams/core/numpy/_gams2numpy.cp312-win_amd64.pyd +0 -0
- gams/core/numpy/gams2numpy.py +1048 -0
- gams/core/opt/__init__.py +26 -0
- gams/core/opt/_optcc.cp312-win_amd64.pyd +0 -0
- gams/core/opt/optcc.py +840 -0
- gams/engine/__init__.py +204 -0
- gams/engine/api/__init__.py +13 -0
- gams/engine/api/auth_api.py +7653 -0
- gams/engine/api/cleanup_api.py +751 -0
- gams/engine/api/default_api.py +887 -0
- gams/engine/api/hypercube_api.py +2629 -0
- gams/engine/api/jobs_api.py +5229 -0
- gams/engine/api/licenses_api.py +2220 -0
- gams/engine/api/namespaces_api.py +7783 -0
- gams/engine/api/usage_api.py +5627 -0
- gams/engine/api/users_api.py +5931 -0
- gams/engine/api_client.py +804 -0
- gams/engine/api_response.py +21 -0
- gams/engine/configuration.py +601 -0
- gams/engine/exceptions.py +216 -0
- gams/engine/models/__init__.py +86 -0
- gams/engine/models/bad_input.py +89 -0
- gams/engine/models/cleanable_job_result.py +104 -0
- gams/engine/models/cleanable_job_result_page.py +113 -0
- gams/engine/models/engine_license.py +107 -0
- gams/engine/models/files_not_found.py +93 -0
- gams/engine/models/forwarded_token_response.py +112 -0
- gams/engine/models/generic_key_value_pair.py +89 -0
- gams/engine/models/hypercube.py +160 -0
- gams/engine/models/hypercube_page.py +111 -0
- gams/engine/models/hypercube_summary.py +91 -0
- gams/engine/models/hypercube_token.py +97 -0
- gams/engine/models/identity_provider.py +107 -0
- gams/engine/models/identity_provider_ldap.py +121 -0
- gams/engine/models/identity_provider_oauth2.py +146 -0
- gams/engine/models/identity_provider_oauth2_scope.py +89 -0
- gams/engine/models/identity_provider_oauth2_with_secret.py +152 -0
- gams/engine/models/identity_provider_oidc.py +133 -0
- gams/engine/models/identity_provider_oidc_with_secret.py +143 -0
- gams/engine/models/inex.py +91 -0
- gams/engine/models/invitation.py +136 -0
- gams/engine/models/invitation_quota.py +106 -0
- gams/engine/models/invitation_token.py +87 -0
- gams/engine/models/job.py +165 -0
- gams/engine/models/job_no_text_entry.py +138 -0
- gams/engine/models/job_no_text_entry_page.py +111 -0
- gams/engine/models/license.py +91 -0
- gams/engine/models/log_piece.py +96 -0
- gams/engine/models/message.py +87 -0
- gams/engine/models/message_and_token.py +99 -0
- gams/engine/models/message_with_webhook_id.py +89 -0
- gams/engine/models/model_auth_token.py +87 -0
- gams/engine/models/model_configuration.py +125 -0
- gams/engine/models/model_default_instance.py +99 -0
- gams/engine/models/model_default_user_instance.py +98 -0
- gams/engine/models/model_hypercube_job.py +106 -0
- gams/engine/models/model_hypercube_usage.py +130 -0
- gams/engine/models/model_instance_info.py +116 -0
- gams/engine/models/model_instance_info_full.py +123 -0
- gams/engine/models/model_instance_pool_info.py +112 -0
- gams/engine/models/model_job_labels.py +179 -0
- gams/engine/models/model_job_usage.py +133 -0
- gams/engine/models/model_pool_usage.py +124 -0
- gams/engine/models/model_usage.py +115 -0
- gams/engine/models/model_user.py +96 -0
- gams/engine/models/model_userinstance_info.py +119 -0
- gams/engine/models/model_userinstancepool_info.py +95 -0
- gams/engine/models/model_version.py +91 -0
- gams/engine/models/models.py +120 -0
- gams/engine/models/namespace.py +104 -0
- gams/engine/models/namespace_quota.py +96 -0
- gams/engine/models/namespace_with_permission.py +96 -0
- gams/engine/models/not_found.py +91 -0
- gams/engine/models/password_policy.py +97 -0
- gams/engine/models/perm_and_username.py +89 -0
- gams/engine/models/quota.py +117 -0
- gams/engine/models/quota_exceeded.py +97 -0
- gams/engine/models/status_code_meaning.py +89 -0
- gams/engine/models/stream_entry.py +89 -0
- gams/engine/models/system_wide_license.py +92 -0
- gams/engine/models/text_entries.py +87 -0
- gams/engine/models/text_entry.py +101 -0
- gams/engine/models/time_span.py +95 -0
- gams/engine/models/time_span_pool_worker.py +99 -0
- gams/engine/models/token_forward_error.py +87 -0
- gams/engine/models/user.py +127 -0
- gams/engine/models/user_group_member.py +96 -0
- gams/engine/models/user_groups.py +108 -0
- gams/engine/models/vapid_info.py +87 -0
- gams/engine/models/webhook.py +138 -0
- gams/engine/models/webhook_parameterized_event.py +99 -0
- gams/engine/py.typed +0 -0
- gams/engine/rest.py +258 -0
- gams/magic/__init__.py +32 -0
- gams/magic/gams_magic.py +142 -0
- gams/magic/interactive.py +402 -0
- gams/tools/__init__.py +30 -0
- gams/tools/errors.py +34 -0
- gams/tools/toolcollection/__init__.py +24 -0
- gams/tools/toolcollection/alg/__init__.py +24 -0
- gams/tools/toolcollection/alg/rank.py +51 -0
- gams/tools/toolcollection/data/__init__.py +24 -0
- gams/tools/toolcollection/data/csvread.py +444 -0
- gams/tools/toolcollection/data/csvwrite.py +311 -0
- gams/tools/toolcollection/data/exceldump.py +47 -0
- gams/tools/toolcollection/data/sqlitewrite.py +276 -0
- gams/tools/toolcollection/gdxservice/__init__.py +24 -0
- gams/tools/toolcollection/gdxservice/gdxencoding.py +104 -0
- gams/tools/toolcollection/gdxservice/gdxrename.py +94 -0
- gams/tools/toolcollection/linalg/__init__.py +24 -0
- gams/tools/toolcollection/linalg/cholesky.py +57 -0
- gams/tools/toolcollection/linalg/eigenvalue.py +56 -0
- gams/tools/toolcollection/linalg/eigenvector.py +58 -0
- gams/tools/toolcollection/linalg/invert.py +55 -0
- gams/tools/toolcollection/linalg/ols.py +138 -0
- gams/tools/toolcollection/tooltemplate.py +321 -0
- gams/tools/toolcollection/win32/__init__.py +24 -0
- gams/tools/toolcollection/win32/excelmerge.py +93 -0
- gams/tools/toolcollection/win32/exceltalk.py +76 -0
- gams/tools/toolcollection/win32/msappavail.py +49 -0
- gams/tools/toolcollection/win32/shellexecute.py +54 -0
- gams/tools/tools.py +116 -0
- gams/transfer/__init__.py +35 -0
- gams/transfer/_abcs/__init__.py +37 -0
- gams/transfer/_abcs/container_abcs.py +433 -0
- gams/transfer/_internals/__init__.py +63 -0
- gams/transfer/_internals/algorithms.py +436 -0
- gams/transfer/_internals/casepreservingdict.py +124 -0
- gams/transfer/_internals/constants.py +270 -0
- gams/transfer/_internals/domainviolation.py +103 -0
- gams/transfer/_internals/specialvalues.py +172 -0
- gams/transfer/containers/__init__.py +26 -0
- gams/transfer/containers/_container.py +1794 -0
- gams/transfer/containers/_io/__init__.py +28 -0
- gams/transfer/containers/_io/containers.py +164 -0
- gams/transfer/containers/_io/gdx.py +1029 -0
- gams/transfer/containers/_io/gmd.py +872 -0
- gams/transfer/containers/_mixins/__init__.py +26 -0
- gams/transfer/containers/_mixins/ccc.py +1274 -0
- gams/transfer/syms/__init__.py +33 -0
- gams/transfer/syms/_methods/__init__.py +24 -0
- gams/transfer/syms/_methods/tables.py +120 -0
- gams/transfer/syms/_methods/toDict.py +115 -0
- gams/transfer/syms/_methods/toList.py +83 -0
- gams/transfer/syms/_methods/toValue.py +60 -0
- gams/transfer/syms/_mixins/__init__.py +32 -0
- gams/transfer/syms/_mixins/equals.py +626 -0
- gams/transfer/syms/_mixins/generateRecords.py +499 -0
- gams/transfer/syms/_mixins/pivot.py +313 -0
- gams/transfer/syms/_mixins/pve.py +627 -0
- gams/transfer/syms/_mixins/sa.py +27 -0
- gams/transfer/syms/_mixins/sapve.py +27 -0
- gams/transfer/syms/_mixins/saua.py +27 -0
- gams/transfer/syms/_mixins/sauapve.py +199 -0
- gams/transfer/syms/_mixins/spve.py +1528 -0
- gams/transfer/syms/_mixins/ve.py +936 -0
- gams/transfer/syms/container_syms/__init__.py +31 -0
- gams/transfer/syms/container_syms/_alias.py +984 -0
- gams/transfer/syms/container_syms/_equation.py +333 -0
- gams/transfer/syms/container_syms/_parameter.py +973 -0
- gams/transfer/syms/container_syms/_set.py +604 -0
- gams/transfer/syms/container_syms/_universe_alias.py +461 -0
- gams/transfer/syms/container_syms/_variable.py +321 -0
- gamsapi-52.5.0.dist-info/METADATA +150 -0
- gamsapi-52.5.0.dist-info/RECORD +257 -0
- gamsapi-52.5.0.dist-info/WHEEL +5 -0
- gamsapi-52.5.0.dist-info/licenses/LICENSE +22 -0
- gamsapi-52.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,276 @@
|
|
|
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 gams.transfer as gt
|
|
27
|
+
from gams.connect.agents.connectagent import ConnectAgent
|
|
28
|
+
from gams.connect.agents._sqlconnectors import (
|
|
29
|
+
AccessConnector,
|
|
30
|
+
MySQLConnector,
|
|
31
|
+
PostgresConnector,
|
|
32
|
+
PyodbcConnector,
|
|
33
|
+
SQLAlchemyConnector,
|
|
34
|
+
SQLiteConnector,
|
|
35
|
+
SQLServerConnector,
|
|
36
|
+
)
|
|
37
|
+
from gams.connect.agents._sqlconnectors._databasehandler import ConnectionType
|
|
38
|
+
from gams.connect.connectvalidator import ConnectValidator
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SQLWriter(ConnectAgent):
|
|
42
|
+
def __init__(self, cdb, inst, agent_index):
|
|
43
|
+
super().__init__(cdb, inst, agent_index)
|
|
44
|
+
self._parse_options(self._inst)
|
|
45
|
+
|
|
46
|
+
def _parse_options(self, inst):
|
|
47
|
+
# global options
|
|
48
|
+
self._input_cnctn = inst["connection"] # input db credentials/path thru user.
|
|
49
|
+
self._cnctn_type = inst["connectionType"]
|
|
50
|
+
self._connection_args = self._dict_get(inst, "connectionArguments", {})
|
|
51
|
+
self._ifExists = inst["ifExists"]
|
|
52
|
+
self._schema_name = inst["schemaName"]
|
|
53
|
+
self._to_sql_args = inst["toSQLArguments"]
|
|
54
|
+
self._trace = inst["trace"]
|
|
55
|
+
self._insertMethod = inst["insertMethod"]
|
|
56
|
+
self._unstack = inst["unstack"]
|
|
57
|
+
self._value_sub = inst["valueSubstitutions"]
|
|
58
|
+
self._dtype_map = inst["dTypeMap"]
|
|
59
|
+
self._col_encloser = inst["columnEncloser"]
|
|
60
|
+
self._skip_text = inst["skipText"]
|
|
61
|
+
self._fast = (
|
|
62
|
+
False if self._cnctn_type != ConnectionType.SQLITE.value else inst["fast"]
|
|
63
|
+
) # NO effect for rest of the connectionTypes
|
|
64
|
+
self._small = (
|
|
65
|
+
False if self._cnctn_type != ConnectionType.SQLITE.value else inst["small"]
|
|
66
|
+
) # NO effect for rest of the connectionTypes
|
|
67
|
+
self._symbols = inst["symbols"] # symbol option
|
|
68
|
+
self._write_all = self._symbols == "all"
|
|
69
|
+
self._sqlitewrite_globalCommit = self._connection_args.pop(
|
|
70
|
+
"__globalCommit__", False
|
|
71
|
+
)
|
|
72
|
+
self._handler = self._get_handler(self._cnctn_type)
|
|
73
|
+
|
|
74
|
+
def _get_handler(
|
|
75
|
+
self, cnctn_type
|
|
76
|
+
) -> (
|
|
77
|
+
SQLiteConnector
|
|
78
|
+
| SQLAlchemyConnector
|
|
79
|
+
| PyodbcConnector
|
|
80
|
+
| MySQLConnector
|
|
81
|
+
| PostgresConnector
|
|
82
|
+
| SQLServerConnector
|
|
83
|
+
| AccessConnector
|
|
84
|
+
):
|
|
85
|
+
handlers = {
|
|
86
|
+
ConnectionType.SQLITE.value: SQLiteConnector,
|
|
87
|
+
ConnectionType.SQLALCHEMY.value: SQLAlchemyConnector,
|
|
88
|
+
ConnectionType.PYODBC.value: PyodbcConnector,
|
|
89
|
+
ConnectionType.MYSQL.value: MySQLConnector,
|
|
90
|
+
ConnectionType.POSTGRES.value: PostgresConnector,
|
|
91
|
+
ConnectionType.SQLSERVER.value: SQLServerConnector,
|
|
92
|
+
ConnectionType.ACCESS.value: AccessConnector,
|
|
93
|
+
}
|
|
94
|
+
handler_class = handlers[cnctn_type]
|
|
95
|
+
|
|
96
|
+
return handler_class(
|
|
97
|
+
error_callback=self._connect_error,
|
|
98
|
+
printLog_callback=self._cdb.print_log,
|
|
99
|
+
trace=self._trace,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def _open(self):
|
|
103
|
+
options = {
|
|
104
|
+
"_sqlitewrite_globalCommit": self._sqlitewrite_globalCommit,
|
|
105
|
+
"small": self._small,
|
|
106
|
+
"fast": self._fast,
|
|
107
|
+
}
|
|
108
|
+
self._handler.connect(
|
|
109
|
+
connection_details=self._input_cnctn,
|
|
110
|
+
connection_args=self._connection_args,
|
|
111
|
+
**options,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def execute(self):
|
|
115
|
+
if self._trace > 0:
|
|
116
|
+
self._log_instructions(self._inst, self._inst_raw)
|
|
117
|
+
self._describe_container(self._cdb.container, "Connect Container:")
|
|
118
|
+
|
|
119
|
+
self._handler.validate_insert_method(method=self._insertMethod)
|
|
120
|
+
self._open()
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
self._handler.create_transaction()
|
|
124
|
+
|
|
125
|
+
if self._write_all:
|
|
126
|
+
self._symbols = []
|
|
127
|
+
sym_schema = self._cdb.load_schema(self)["symbols"]["oneof"][1][
|
|
128
|
+
"schema"
|
|
129
|
+
]["schema"]
|
|
130
|
+
v = ConnectValidator(sym_schema)
|
|
131
|
+
for name, sym in self._cdb.container.data.items():
|
|
132
|
+
if type(sym) in [gt.Set, gt.Parameter]:
|
|
133
|
+
sym_inst = v.validated({"name": name, "tableName": name}) # type: ignore
|
|
134
|
+
if sym_inst is None:
|
|
135
|
+
self._connect_error(
|
|
136
|
+
f"Validation for symbol >{name}< failed: {v.errors}" # type: ignore
|
|
137
|
+
)
|
|
138
|
+
sym_inst = v.normalize_of_rules(sym_inst)
|
|
139
|
+
self._symbols.append(sym_inst)
|
|
140
|
+
|
|
141
|
+
symbols_raw = self._symbols.copy()
|
|
142
|
+
sym_list = []
|
|
143
|
+
for s in self._symbols:
|
|
144
|
+
sym_name = s["name"]
|
|
145
|
+
self._symbols_exist_cdb(sym_name, should_exist=True)
|
|
146
|
+
self._update_sym_inst(s, self._inst)
|
|
147
|
+
sym_list.append(s["name"])
|
|
148
|
+
|
|
149
|
+
pre_write_args = {
|
|
150
|
+
# only implemented for sqlite, pre_write_procedures must return the container
|
|
151
|
+
"container": self._cdb.container,
|
|
152
|
+
"directory": self._system_directory,
|
|
153
|
+
"sym_list": sym_list,
|
|
154
|
+
}
|
|
155
|
+
write_container = self._handler.pre_write_procedures(**pre_write_args)
|
|
156
|
+
|
|
157
|
+
for sym, sym_raw in zip(self._symbols, symbols_raw):
|
|
158
|
+
if self._trace > 0:
|
|
159
|
+
self._log_instructions(
|
|
160
|
+
sym, sym_raw, description=f"Write symbol >{sym['name']}<:"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
sym_name = sym["name"]
|
|
164
|
+
table_name = sym["tableName"]
|
|
165
|
+
schema = sym["schemaName"]
|
|
166
|
+
exists = sym["ifExists"]
|
|
167
|
+
unstack = sym["unstack"]
|
|
168
|
+
value_sub = sym["valueSubstitutions"]
|
|
169
|
+
dtype_map = self._dict_get(sym, "dTypeMap", {})
|
|
170
|
+
insertMethod = sym["insertMethod"]
|
|
171
|
+
skip_text = sym["skipText"]
|
|
172
|
+
self._handler.validate_insert_method(method=insertMethod)
|
|
173
|
+
|
|
174
|
+
if self._small and table_name == "UEL$":
|
|
175
|
+
self._connect_error(
|
|
176
|
+
"tableName >UEL$< is not allowed. >UEL$< is a preserved tableName with small set to >True<."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
gt_sym = write_container[sym_name]
|
|
180
|
+
|
|
181
|
+
if self._trace > 2:
|
|
182
|
+
self._cdb.print_log(
|
|
183
|
+
f"Connect Container symbol={sym_name}:\n {gt_sym.records}\n" # type: ignore
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if not isinstance(gt_sym, gt.Set) and not isinstance(
|
|
187
|
+
gt_sym, gt.Parameter
|
|
188
|
+
):
|
|
189
|
+
self._connect_error(
|
|
190
|
+
f"Symbol type >{type(gt_sym)}< of symbol >{sym_name}< is not supported. Supported symbol types are set and parameter."
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
dim = gt_sym.dimension
|
|
194
|
+
df = self._sym_records_no_none(gt_sym).copy(deep=True)
|
|
195
|
+
sym_type = "par" if isinstance(gt_sym, gt.Parameter) else "set"
|
|
196
|
+
value = "value" if sym_type == "par" else "element_text"
|
|
197
|
+
|
|
198
|
+
if value_sub:
|
|
199
|
+
df = self._apply_value_substitutions(df, value_sub, sym_type)
|
|
200
|
+
if self._trace > 2:
|
|
201
|
+
self._cdb.print_log(f"After value substitution:\n{df}")
|
|
202
|
+
|
|
203
|
+
if unstack and dim > 0:
|
|
204
|
+
if (
|
|
205
|
+
sym_type == "set" and skip_text
|
|
206
|
+
): # replace all element_text by Y when exporting a true table
|
|
207
|
+
df.loc[:, value] = "Y"
|
|
208
|
+
elif (
|
|
209
|
+
sym_type == "set"
|
|
210
|
+
): # replace empty element_text by Y when exporting a true table
|
|
211
|
+
df.loc[df[value] == "", value] = "Y"
|
|
212
|
+
cols = list(df.columns)
|
|
213
|
+
if dim > 1:
|
|
214
|
+
df = df.pivot(index=cols[0:-2], columns=cols[-2], values=value)
|
|
215
|
+
df.reset_index(inplace=True, drop=False)
|
|
216
|
+
elif len(df) > 0:
|
|
217
|
+
df = df.set_index(cols[0]).T.reset_index(drop=True)
|
|
218
|
+
else:
|
|
219
|
+
self._connect_error(
|
|
220
|
+
f"unstack: >{unstack}< on 1-dimensional symbol with empty DataFrame not allowed."
|
|
221
|
+
)
|
|
222
|
+
df.rename_axis(
|
|
223
|
+
[None], axis=1, inplace=True
|
|
224
|
+
) # remove column index names
|
|
225
|
+
if self._trace > 2:
|
|
226
|
+
self._cdb.print_log(f"DataFrame after unstack:\n{df}")
|
|
227
|
+
|
|
228
|
+
elif dim > 0:
|
|
229
|
+
df.sort_values(df.columns[:-1].tolist(), inplace=True)
|
|
230
|
+
if self._trace > 2:
|
|
231
|
+
self._cdb.print_log(f"DataFrame after sort:\n{df}")
|
|
232
|
+
if sym_type == "set" and skip_text:
|
|
233
|
+
df = df.drop(columns="element_text")
|
|
234
|
+
|
|
235
|
+
writeFunction_args = {
|
|
236
|
+
"name": table_name,
|
|
237
|
+
"schema": schema,
|
|
238
|
+
"if_exists": exists,
|
|
239
|
+
"insertMethod": insertMethod,
|
|
240
|
+
"columnEncloser": self._col_encloser,
|
|
241
|
+
"dtype_map": dtype_map,
|
|
242
|
+
"toSQLArguments": self._dict_get(sym, "toSQLArguments", {}),
|
|
243
|
+
"index": False,
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
dim_after_unstack = None
|
|
247
|
+
if self._small and dim > 0:
|
|
248
|
+
dim_after_unstack = dim - 1 if unstack else dim
|
|
249
|
+
col = df.columns[:dim_after_unstack]
|
|
250
|
+
df[col] = df[col].astype("int64")
|
|
251
|
+
writeFunction_args.update({"name": f"[{table_name}$]"})
|
|
252
|
+
|
|
253
|
+
self._handler.write_dataframe(df, writeFunction_args)
|
|
254
|
+
|
|
255
|
+
post_write_args = {
|
|
256
|
+
"tableName": table_name,
|
|
257
|
+
"dim_after_unstack": dim_after_unstack,
|
|
258
|
+
"colList": df.columns.tolist(),
|
|
259
|
+
"dim": dim,
|
|
260
|
+
}
|
|
261
|
+
self._handler.post_write_procedures(
|
|
262
|
+
**post_write_args
|
|
263
|
+
) # only implemented for sqlite
|
|
264
|
+
|
|
265
|
+
if not self._sqlitewrite_globalCommit:
|
|
266
|
+
self._handler.commit()
|
|
267
|
+
|
|
268
|
+
if self._sqlitewrite_globalCommit:
|
|
269
|
+
self._handler.commit()
|
|
270
|
+
|
|
271
|
+
except Exception:
|
|
272
|
+
self._handler.rollback()
|
|
273
|
+
raise
|
|
274
|
+
|
|
275
|
+
finally:
|
|
276
|
+
self._handler.close()
|
|
@@ -0,0 +1,275 @@
|
|
|
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 importlib
|
|
27
|
+
import sys
|
|
28
|
+
import weakref
|
|
29
|
+
import yaml
|
|
30
|
+
from gams.connect.agents.connectagent import ConnectAgent
|
|
31
|
+
from gams.connect.connectvalidator import ConnectValidator
|
|
32
|
+
from gams.connect.errors import GamsConnectException
|
|
33
|
+
from gams.control import GamsWorkspace
|
|
34
|
+
from gams.transfer import Container
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ConnectDatabase(object):
|
|
38
|
+
"""
|
|
39
|
+
A ConnectDatabase contains data in the form of a gams.transfer.Container instance.
|
|
40
|
+
Running the execute() method instantiates Connect agents that read, write, or modify
|
|
41
|
+
symbol data.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
system_directory : str
|
|
46
|
+
GAMS system directory to be used.
|
|
47
|
+
container : gams.transfer.Container, optional
|
|
48
|
+
A Container to be used by the ConnectDatabase, by default None. If omitted, the ConnectDatabase will instantiate a new and empty container.
|
|
49
|
+
ecdb : gams.core.embedded.ECGAMSDatabase, optional
|
|
50
|
+
When running in a GAMS context (e.g. embedded code), this can be used to allow connection to the embedded code GAMS database, by default None.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, system_directory, container=None, ecdb=None):
|
|
54
|
+
self._system_directory = system_directory
|
|
55
|
+
self._ecdb = ecdb
|
|
56
|
+
self._schema_cache = {}
|
|
57
|
+
self._ws = GamsWorkspace(system_directory=self._system_directory)
|
|
58
|
+
if container is None:
|
|
59
|
+
self._container = Container(system_directory=self._system_directory)
|
|
60
|
+
else:
|
|
61
|
+
self._container = container
|
|
62
|
+
if self._ecdb:
|
|
63
|
+
ecdb._cdb = self
|
|
64
|
+
|
|
65
|
+
def __del__(self):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def print_log(self, msg, end="\n"):
|
|
69
|
+
"""
|
|
70
|
+
Print msg to the GAMS log if avaiable, uses print() otherwise.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
msg : str
|
|
75
|
+
The message to be printed.
|
|
76
|
+
end : str, optional
|
|
77
|
+
String to be put after the message, by default "\n".
|
|
78
|
+
"""
|
|
79
|
+
if self._ecdb:
|
|
80
|
+
self._ecdb.printLog(msg, end)
|
|
81
|
+
else:
|
|
82
|
+
print(msg, end=end)
|
|
83
|
+
sys.stdout.flush()
|
|
84
|
+
|
|
85
|
+
def _get_agent_class(self, agent_name):
|
|
86
|
+
# Try to load the agent module from 'gams.connect.agents'
|
|
87
|
+
try:
|
|
88
|
+
mod = importlib.import_module("gams.connect.agents." + agent_name.lower())
|
|
89
|
+
err = None
|
|
90
|
+
except ModuleNotFoundError as e:
|
|
91
|
+
err = e
|
|
92
|
+
if err:
|
|
93
|
+
try:
|
|
94
|
+
if (
|
|
95
|
+
err.name != "gams.connect.agents." + agent_name.lower()
|
|
96
|
+
): # the connect agent module itself was found but an import in the source itself did fail
|
|
97
|
+
raise GamsConnectException(str(err), traceback=True)
|
|
98
|
+
mod = importlib.import_module(agent_name.lower())
|
|
99
|
+
err = None
|
|
100
|
+
except ModuleNotFoundError as e:
|
|
101
|
+
err = e
|
|
102
|
+
|
|
103
|
+
# Try to retrieve the agent class from the found module
|
|
104
|
+
if not err:
|
|
105
|
+
try:
|
|
106
|
+
agent_class = vars(mod)[agent_name]
|
|
107
|
+
except KeyError as e:
|
|
108
|
+
err = e
|
|
109
|
+
|
|
110
|
+
# Compile error in case of previous errors
|
|
111
|
+
if err:
|
|
112
|
+
msg = f"Connect agent '{agent_name}' not found. The following agents are available: {', '.join(self._get_available_agents())}."
|
|
113
|
+
raise GamsConnectException(msg)
|
|
114
|
+
|
|
115
|
+
return agent_class
|
|
116
|
+
|
|
117
|
+
def _get_idx_str(self, agent_name, idx_list):
|
|
118
|
+
idx_str = f"{agent_name}"
|
|
119
|
+
if idx_list:
|
|
120
|
+
idx_str += f"({idx_list[0]})"
|
|
121
|
+
for i in idx_list[1:]:
|
|
122
|
+
idx_str = f"PythonCode({i})" + "->" + idx_str
|
|
123
|
+
return idx_str
|
|
124
|
+
|
|
125
|
+
def _execute(self, instructions, idx_list=[]):
|
|
126
|
+
if isinstance(instructions, list):
|
|
127
|
+
if all(isinstance(inst, dict) for inst in instructions):
|
|
128
|
+
inst_list = instructions
|
|
129
|
+
else:
|
|
130
|
+
raise GamsConnectException(
|
|
131
|
+
"Invalid data type for instructions argument. Needs to be 'list of dict'."
|
|
132
|
+
)
|
|
133
|
+
elif isinstance(instructions, dict):
|
|
134
|
+
inst_list = [instructions]
|
|
135
|
+
else:
|
|
136
|
+
raise GamsConnectException(
|
|
137
|
+
f"Invalid data type for instructions argument. Needs to be 'dict' or 'list', but was '{type(instructions).__name__}'."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
agent_instances = []
|
|
141
|
+
for idx, inst in enumerate(inst_list, start=1):
|
|
142
|
+
root = list(inst.keys())
|
|
143
|
+
if len(root) != 1:
|
|
144
|
+
raise GamsConnectException(
|
|
145
|
+
f"Invalid agent definition with {len(root)} agent names instead of 1."
|
|
146
|
+
)
|
|
147
|
+
agent_name = root[0]
|
|
148
|
+
if not isinstance(agent_name, str):
|
|
149
|
+
raise GamsConnectException(
|
|
150
|
+
f"Invalid data type for agent name. Needs to be 'str', but was '{type(agent_name).__name__}'."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
inst = inst[agent_name]
|
|
154
|
+
|
|
155
|
+
agent_class = self._get_agent_class(agent_name)
|
|
156
|
+
if not issubclass(agent_class, ConnectAgent):
|
|
157
|
+
raise GamsConnectException(
|
|
158
|
+
f"Agent class '{agent_name}' has to be derived from gams.connect.agents.connectagent.ConnectAgent",
|
|
159
|
+
traceback=True,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
agent_schema = self.load_schema(agent_class.__name__)
|
|
163
|
+
v = ConnectValidator(agent_schema)
|
|
164
|
+
if not v.validate(inst):
|
|
165
|
+
raise GamsConnectException(
|
|
166
|
+
f"Validation of instructions for agent {self._get_idx_str(agent_name, [idx] + idx_list)} failed: {v.errors}"
|
|
167
|
+
)
|
|
168
|
+
agent_instance = agent_class(self, inst, [idx] + idx_list)
|
|
169
|
+
agent_instances.append(agent_instance)
|
|
170
|
+
|
|
171
|
+
for idx, agent in enumerate(agent_instances, start=1):
|
|
172
|
+
agent.setup_log()
|
|
173
|
+
try:
|
|
174
|
+
execute_return = agent.execute()
|
|
175
|
+
except GamsConnectException:
|
|
176
|
+
raise
|
|
177
|
+
except Exception as e:
|
|
178
|
+
e.add_note(self._get_idx_str(agent._agent_name, agent._agent_index) + " failed")
|
|
179
|
+
raise
|
|
180
|
+
agent.restore_log()
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
type(agent).__name__ == "PythonCode" and execute_return
|
|
184
|
+
): # PythonCode generated instructions
|
|
185
|
+
self._execute(execute_return, [idx] + idx_list)
|
|
186
|
+
|
|
187
|
+
def execute(self, instructions):
|
|
188
|
+
"""
|
|
189
|
+
Instantiates and executes one or multiple Connect agents.
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
instructions : list, dict
|
|
194
|
+
The instructions to be used for instantiating and executing the agents. Use list for executnig multiple agents and dict for a single one.
|
|
195
|
+
|
|
196
|
+
Raises
|
|
197
|
+
----------
|
|
198
|
+
GamsConnectException
|
|
199
|
+
If the instructions are invalid or if the specified agent could not be loaded.
|
|
200
|
+
"""
|
|
201
|
+
self._execute(instructions)
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def container(self):
|
|
205
|
+
return self._container
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def ecdb(self):
|
|
209
|
+
return self._ecdb
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def system_directory(self):
|
|
213
|
+
return self._system_directory
|
|
214
|
+
|
|
215
|
+
def _get_available_agents(self):
|
|
216
|
+
try:
|
|
217
|
+
agents = []
|
|
218
|
+
with importlib.resources.as_file(
|
|
219
|
+
importlib.resources.files("gams.connect.agents") / "schema"
|
|
220
|
+
) as schema_path:
|
|
221
|
+
for f in schema_path.rglob('*.yaml'):
|
|
222
|
+
if f.is_file():
|
|
223
|
+
agents.append(f.name.split(".")[0])
|
|
224
|
+
return agents
|
|
225
|
+
except:
|
|
226
|
+
return []
|
|
227
|
+
|
|
228
|
+
#def _get_close_agent(self, agent_name):
|
|
229
|
+
# import difflib
|
|
230
|
+
# agents = self._get_available_agents()
|
|
231
|
+
# agents = {a.lower(): a for a in agents} # use case-insensitive comparison since otherwise "csvwriter" -> "ExcelWriter" instead of "CSVWriter
|
|
232
|
+
# m = difflib.get_close_matches(agent_name.lower(), agents.keys(), n=1, cutoff=0.7)
|
|
233
|
+
# return agents[m[0]] if m else None
|
|
234
|
+
|
|
235
|
+
def load_schema(self, agent):
|
|
236
|
+
"""
|
|
237
|
+
Returns the cerberus schema for a specific agent as Python data structure.
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
agent : str, ConnectAgent
|
|
242
|
+
Either the name of the Connect agent or the ConnectAgent instance itself.
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
dict
|
|
247
|
+
Python data structure to be used for validation to ensure correct format of instructions given to a Connect agent.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
if isinstance(agent, str):
|
|
251
|
+
agent_name = agent
|
|
252
|
+
elif isinstance(agent, ConnectAgent):
|
|
253
|
+
agent_name = agent.__class__.__name__
|
|
254
|
+
else:
|
|
255
|
+
raise GamsConnectException(
|
|
256
|
+
f"Parameter 'agent' needs to be of type str or ConnectAgent, but was {type(agent)}."
|
|
257
|
+
)
|
|
258
|
+
with importlib.resources.as_file(
|
|
259
|
+
importlib.resources.files("gams.connect.agents") / "schema"
|
|
260
|
+
) as schema_path:
|
|
261
|
+
schema_file = schema_path / (agent_name + ".yaml")
|
|
262
|
+
fstat = schema_file.stat()
|
|
263
|
+
mtime = fstat.st_mtime
|
|
264
|
+
size = fstat.st_size
|
|
265
|
+
if (
|
|
266
|
+
agent_name in self._schema_cache
|
|
267
|
+
and mtime == self._schema_cache[agent_name]["mtime"]
|
|
268
|
+
and size == self._schema_cache[agent_name]["size"]
|
|
269
|
+
):
|
|
270
|
+
schema = self._schema_cache[agent_name]["schema"]
|
|
271
|
+
else:
|
|
272
|
+
schema = schema_file.read_text()
|
|
273
|
+
schema = yaml.safe_load(schema)
|
|
274
|
+
self._schema_cache[agent_name] = {"schema": schema, "mtime": mtime, "size": size}
|
|
275
|
+
return schema
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
from cerberus import Validator
|
|
27
|
+
from cerberus.platform import Hashable
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ConnectValidator(Validator):
|
|
31
|
+
|
|
32
|
+
def __init__(self, *args, **kwargs):
|
|
33
|
+
super(ConnectValidator, self).__init__(*args, **kwargs)
|
|
34
|
+
|
|
35
|
+
# Custom validator that does not consider null values when validating excludes rule
|
|
36
|
+
def _validate_excludes(self, excluded_fields, field, value):
|
|
37
|
+
"""{'type': ('hashable', 'list'), 'schema': {'type': 'hashable'}}"""
|
|
38
|
+
if isinstance(excluded_fields, Hashable):
|
|
39
|
+
excluded_fields = [excluded_fields]
|
|
40
|
+
|
|
41
|
+
# get all fields from excluded_fields which values are not None
|
|
42
|
+
not_none_fields = [
|
|
43
|
+
ef
|
|
44
|
+
for ef in excluded_fields
|
|
45
|
+
# if self.document.get(ef) != self.schema[ef]["default"]
|
|
46
|
+
if self.document.get(ef) is not None
|
|
47
|
+
]
|
|
48
|
+
# if value == self.schema[field]["default"]:
|
|
49
|
+
if value is None: # None value is considered to be the same as missing
|
|
50
|
+
if (
|
|
51
|
+
not_none_fields
|
|
52
|
+
): # if all other fields are None or missing as well, we can skip validation
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
# if not_none_fields has values, then we want to get the default behavior for those fields only
|
|
56
|
+
super()._validate_excludes(not_none_fields, field, value)
|
|
57
|
+
|
|
58
|
+
def normalize_of_rules(self, document):
|
|
59
|
+
"""
|
|
60
|
+
Perform normalization of 'anyof' and 'oneof' on the first
|
|
61
|
+
level of the document only. The implementation looks for 'anyof' and 'oneof'
|
|
62
|
+
in the schema and finds the first sub schema that matches. This needs to be
|
|
63
|
+
considered especially for 'anyof' since multiple sub schemas are allowed to
|
|
64
|
+
match. The determined sub schema will be used to perform the normalization.
|
|
65
|
+
:param document: the document to be normalized
|
|
66
|
+
:return: the normalized document
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
document_copy = dict(document)
|
|
70
|
+
for k, v in self.schema.items():
|
|
71
|
+
if document[k] is not None:
|
|
72
|
+
is_list = v.get("type") == "list"
|
|
73
|
+
of_rule = v
|
|
74
|
+
if is_list:
|
|
75
|
+
of_rule = of_rule.get("schema", {})
|
|
76
|
+
of_rule = of_rule.get("anyof", of_rule.get("oneof"))
|
|
77
|
+
if of_rule is not None:
|
|
78
|
+
document_copy[k] = []
|
|
79
|
+
if is_list:
|
|
80
|
+
doc_list = [{k: x} for x in document[k]]
|
|
81
|
+
else:
|
|
82
|
+
doc_list = [{k: document[k]}]
|
|
83
|
+
for sub_doc in doc_list:
|
|
84
|
+
for sub_schema in of_rule:
|
|
85
|
+
new_schema = {k: sub_schema}
|
|
86
|
+
v = ConnectValidator(new_schema)
|
|
87
|
+
sub_doc_normalized = v.validated(sub_doc)
|
|
88
|
+
if sub_doc_normalized is not None:
|
|
89
|
+
if is_list:
|
|
90
|
+
document_copy[k].append(sub_doc_normalized[k])
|
|
91
|
+
else:
|
|
92
|
+
document_copy[k] = sub_doc_normalized[k]
|
|
93
|
+
return document_copy
|
gams/connect/errors.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
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 sys
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class GamsConnectException(Exception):
|
|
30
|
+
def __init__(self, message, traceback=False):
|
|
31
|
+
super().__init__(message)
|
|
32
|
+
self.traceback = traceback
|
|
33
|
+
if not self.traceback:
|
|
34
|
+
sys.tracebacklimit = 0
|