xian-tech-contracting 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- contracting/__init__.py +0 -0
- contracting/client.py +389 -0
- contracting/compilation/__init__.py +0 -0
- contracting/compilation/compiler.py +155 -0
- contracting/compilation/linter.py +419 -0
- contracting/compilation/parser.py +55 -0
- contracting/compilation/whitelists.py +145 -0
- contracting/constants.py +54 -0
- contracting/contracts/__init__.py +0 -0
- contracting/contracts/proxythis.py +11 -0
- contracting/contracts/submission.s.py +108 -0
- contracting/contracts/thistest2.py +11 -0
- contracting/contracts/zk_registry.s.py +153 -0
- contracting/execution/__init__.py +0 -0
- contracting/execution/executor.py +235 -0
- contracting/execution/module.py +188 -0
- contracting/execution/native_tracer.py +139 -0
- contracting/execution/python_tracer.py +188 -0
- contracting/execution/runtime.py +428 -0
- contracting/execution/tracer.py +57 -0
- contracting/execution/tracer_common.py +282 -0
- contracting/names.py +26 -0
- contracting/stdlib/__init__.py +0 -0
- contracting/stdlib/bridge/__init__.py +0 -0
- contracting/stdlib/bridge/access.py +41 -0
- contracting/stdlib/bridge/crypto.py +35 -0
- contracting/stdlib/bridge/hashing.py +43 -0
- contracting/stdlib/bridge/imports.py +337 -0
- contracting/stdlib/bridge/orm.py +62 -0
- contracting/stdlib/bridge/random.py +128 -0
- contracting/stdlib/bridge/zk.py +274 -0
- contracting/stdlib/env.py +33 -0
- contracting/storage/__init__.py +0 -0
- contracting/storage/contract.py +115 -0
- contracting/storage/driver.py +390 -0
- contracting/storage/lmdb_store.py +137 -0
- contracting/storage/orm.py +428 -0
- xian_tech_contracting-1.0.1.dist-info/METADATA +125 -0
- xian_tech_contracting-1.0.1.dist-info/RECORD +42 -0
- xian_tech_contracting-1.0.1.dist-info/WHEEL +5 -0
- xian_tech_contracting-1.0.1.dist-info/licenses/LICENSE +7 -0
- xian_tech_contracting-1.0.1.dist-info/top_level.txt +1 -0
contracting/__init__.py
ADDED
|
File without changes
|
contracting/client.py
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import inspect
|
|
3
|
+
import os
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from functools import partial
|
|
6
|
+
from types import FunctionType
|
|
7
|
+
|
|
8
|
+
import autopep8
|
|
9
|
+
from xian_runtime_types.time import Datetime
|
|
10
|
+
|
|
11
|
+
from contracting.compilation.compiler import ContractingCompiler
|
|
12
|
+
from contracting.execution import runtime
|
|
13
|
+
from contracting.execution.executor import Executor
|
|
14
|
+
from contracting.storage.driver import Driver
|
|
15
|
+
|
|
16
|
+
from . import constants
|
|
17
|
+
from .storage.orm import Hash, Variable
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AbstractContract:
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
name,
|
|
24
|
+
signer,
|
|
25
|
+
environment,
|
|
26
|
+
executor: Executor,
|
|
27
|
+
funcs,
|
|
28
|
+
return_full_output=False,
|
|
29
|
+
):
|
|
30
|
+
self.name = name
|
|
31
|
+
self.signer = signer
|
|
32
|
+
self.environment = environment
|
|
33
|
+
self.executor = executor
|
|
34
|
+
self.functions = funcs
|
|
35
|
+
|
|
36
|
+
# set up virtual functions
|
|
37
|
+
for f in funcs:
|
|
38
|
+
# unpack tuple packed in SenecaClient
|
|
39
|
+
func, kwargs = f
|
|
40
|
+
|
|
41
|
+
# set the kwargs to None. these will fail if they are not provided
|
|
42
|
+
default_kwargs = {}
|
|
43
|
+
for kwarg in kwargs:
|
|
44
|
+
default_kwargs[kwarg] = None
|
|
45
|
+
|
|
46
|
+
# each function is a partial that allows kwarg overloading and overriding
|
|
47
|
+
setattr(
|
|
48
|
+
self,
|
|
49
|
+
func,
|
|
50
|
+
partial(
|
|
51
|
+
self._abstract_function_call,
|
|
52
|
+
signer=self.signer,
|
|
53
|
+
contract_name=self.name,
|
|
54
|
+
executor=self.executor,
|
|
55
|
+
func=func,
|
|
56
|
+
return_full_output=return_full_output,
|
|
57
|
+
# environment=self.environment,
|
|
58
|
+
**default_kwargs,
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def keys(self):
|
|
63
|
+
# Scope strictly to this contract's namespace
|
|
64
|
+
return self.executor.driver.keys(f"{self.name}.")
|
|
65
|
+
|
|
66
|
+
# a variable contains a DOT, but no __, and no :
|
|
67
|
+
# a hash contains a DOT, no __, and a :
|
|
68
|
+
# a constant contains __, a DOT, and :
|
|
69
|
+
|
|
70
|
+
def quick_read(self, variable, key=None, args=None):
|
|
71
|
+
a = []
|
|
72
|
+
|
|
73
|
+
if key is not None:
|
|
74
|
+
a.append(key)
|
|
75
|
+
|
|
76
|
+
if args is not None and isinstance(args, list):
|
|
77
|
+
for arg in args:
|
|
78
|
+
a.append(arg)
|
|
79
|
+
|
|
80
|
+
k = self.executor.driver.make_key(
|
|
81
|
+
contract=self.name, variable=variable, args=a
|
|
82
|
+
)
|
|
83
|
+
return self.executor.driver.get(k)
|
|
84
|
+
|
|
85
|
+
def quick_write(self, variable, key=None, value=None, args=None):
|
|
86
|
+
if key is not None:
|
|
87
|
+
a = [key]
|
|
88
|
+
else:
|
|
89
|
+
a = []
|
|
90
|
+
|
|
91
|
+
if args is not None and isinstance(args, list):
|
|
92
|
+
for arg in args:
|
|
93
|
+
a.append(arg)
|
|
94
|
+
|
|
95
|
+
k = self.executor.driver.make_key(
|
|
96
|
+
contract=self.name, variable=variable, args=a
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
self.executor.driver.set(k, value)
|
|
100
|
+
self.executor.driver.commit()
|
|
101
|
+
|
|
102
|
+
def run_private_function(self, f, signer=None, environment=None, **kwargs):
|
|
103
|
+
# Override kwargs if provided
|
|
104
|
+
signer = signer or self.signer
|
|
105
|
+
environment = environment or self.environment
|
|
106
|
+
|
|
107
|
+
# Let executor access private functions
|
|
108
|
+
self.executor.bypass_privates = True
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# Append private method prefix to function name if it isn't there already
|
|
112
|
+
if not f.startswith(constants.PRIVATE_METHOD_PREFIX):
|
|
113
|
+
f = "{}{}".format(constants.PRIVATE_METHOD_PREFIX, f)
|
|
114
|
+
|
|
115
|
+
return self._abstract_function_call(
|
|
116
|
+
signer=signer,
|
|
117
|
+
executor=self.executor,
|
|
118
|
+
contract_name=self.name,
|
|
119
|
+
environment=environment,
|
|
120
|
+
func=f,
|
|
121
|
+
metering=None,
|
|
122
|
+
now=None,
|
|
123
|
+
**kwargs,
|
|
124
|
+
)
|
|
125
|
+
finally:
|
|
126
|
+
# Always restore restricted mode, even if the private call fails.
|
|
127
|
+
self.executor.bypass_privates = False
|
|
128
|
+
|
|
129
|
+
def __getattr__(self, item):
|
|
130
|
+
try:
|
|
131
|
+
# return the attribute if it exists on the instance
|
|
132
|
+
return self.__getattribute__(item)
|
|
133
|
+
except AttributeError as e:
|
|
134
|
+
# otherwise, attempt to resolve it. full name is contract.item
|
|
135
|
+
fullname = "{}.{}".format(self.name, item)
|
|
136
|
+
|
|
137
|
+
# if the raw name exists, it is a __protected__ or a variable, so prepare for those
|
|
138
|
+
if fullname in self.keys():
|
|
139
|
+
variable = Variable(
|
|
140
|
+
contract=self.name, name=item, driver=self.executor.driver
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# return just the value if it is __protected__ to prevent sets
|
|
144
|
+
if item.startswith("__"):
|
|
145
|
+
return variable.get()
|
|
146
|
+
|
|
147
|
+
# otherwise, return the variable object with allows sets
|
|
148
|
+
return variable
|
|
149
|
+
|
|
150
|
+
# otherwise, see if contract.items: has more than one entry
|
|
151
|
+
if (
|
|
152
|
+
len(
|
|
153
|
+
self.executor.driver.values(
|
|
154
|
+
prefix=self.name + "." + item + ":"
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
> 0
|
|
158
|
+
):
|
|
159
|
+
# if so, it is a hash. return the hash object
|
|
160
|
+
return Hash(
|
|
161
|
+
contract=self.name, name=item, driver=self.executor.driver
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# otherwise, the attribut does not exist, so throw the error.
|
|
165
|
+
raise e
|
|
166
|
+
|
|
167
|
+
def now(self):
|
|
168
|
+
d = datetime.today()
|
|
169
|
+
return Datetime(d.year, d.month, d.day, hour=d.hour, minute=d.minute)
|
|
170
|
+
|
|
171
|
+
def _abstract_function_call(
|
|
172
|
+
self,
|
|
173
|
+
signer,
|
|
174
|
+
executor,
|
|
175
|
+
contract_name,
|
|
176
|
+
func,
|
|
177
|
+
environment=None,
|
|
178
|
+
stamps=constants.DEFAULT_STAMPS,
|
|
179
|
+
metering=None,
|
|
180
|
+
now=None,
|
|
181
|
+
return_full_output=False,
|
|
182
|
+
**kwargs,
|
|
183
|
+
):
|
|
184
|
+
|
|
185
|
+
# for k, v in kwargs.items():
|
|
186
|
+
# assert v is not None, 'Keyword "{}" not provided. Must not be None.'.format(k)
|
|
187
|
+
environment = environment or self.environment
|
|
188
|
+
|
|
189
|
+
if now is None:
|
|
190
|
+
now = self.now()
|
|
191
|
+
|
|
192
|
+
if environment.get("now") is None:
|
|
193
|
+
environment.update({"now": now})
|
|
194
|
+
|
|
195
|
+
output = executor.execute(
|
|
196
|
+
sender=signer,
|
|
197
|
+
contract_name=contract_name,
|
|
198
|
+
function_name=func,
|
|
199
|
+
kwargs=kwargs,
|
|
200
|
+
stamps=stamps,
|
|
201
|
+
environment=environment,
|
|
202
|
+
metering=metering,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if executor.production:
|
|
206
|
+
executor.sandbox.terminate()
|
|
207
|
+
|
|
208
|
+
if output["status_code"] == 1:
|
|
209
|
+
raise output["result"] if not return_full_output else output
|
|
210
|
+
return output["result"] if not return_full_output else output
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class ContractingClient:
|
|
214
|
+
def __init__(
|
|
215
|
+
self,
|
|
216
|
+
signer="sys",
|
|
217
|
+
submission_filename=os.path.join(
|
|
218
|
+
os.path.dirname(__file__), "contracts/submission.s.py"
|
|
219
|
+
),
|
|
220
|
+
storage_home=constants.STORAGE_HOME,
|
|
221
|
+
driver: Driver = None,
|
|
222
|
+
metering=False,
|
|
223
|
+
compiler=ContractingCompiler(),
|
|
224
|
+
environment={},
|
|
225
|
+
tracer_mode: str | None = None,
|
|
226
|
+
):
|
|
227
|
+
if tracer_mode is not None:
|
|
228
|
+
runtime.rt.set_tracer_mode(tracer_mode)
|
|
229
|
+
driver = (
|
|
230
|
+
driver if driver is not None else Driver(storage_home=storage_home)
|
|
231
|
+
)
|
|
232
|
+
self.executor = Executor(metering=metering, driver=driver)
|
|
233
|
+
self.raw_driver = driver
|
|
234
|
+
self.signer = signer
|
|
235
|
+
self.compiler = compiler
|
|
236
|
+
self.submission_filename = submission_filename
|
|
237
|
+
self.environment = environment
|
|
238
|
+
# Get submission contract from file
|
|
239
|
+
if submission_filename is not None:
|
|
240
|
+
# Seed the genesis contracts into the instance
|
|
241
|
+
with open(self.submission_filename) as f:
|
|
242
|
+
contract = f.read()
|
|
243
|
+
|
|
244
|
+
self.raw_driver.set_contract(name="submission", code=contract)
|
|
245
|
+
self.raw_driver.commit()
|
|
246
|
+
|
|
247
|
+
# Get submission contract from state
|
|
248
|
+
self.submission_contract = self.get_contract("submission")
|
|
249
|
+
|
|
250
|
+
def set_submission_contract(self, filename=None, commit=True):
|
|
251
|
+
state_contract = self.get_contract("submission")
|
|
252
|
+
|
|
253
|
+
if filename is None:
|
|
254
|
+
filename = self.submission_filename
|
|
255
|
+
|
|
256
|
+
if filename is None and state_contract is None:
|
|
257
|
+
raise AssertionError(
|
|
258
|
+
"No submission contract provided or found in state."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if filename is not None:
|
|
262
|
+
with open(filename) as f:
|
|
263
|
+
contract = f.read()
|
|
264
|
+
|
|
265
|
+
self.raw_driver.delete_contract(name="submission")
|
|
266
|
+
self.raw_driver.set_contract(name="submission", code=contract)
|
|
267
|
+
|
|
268
|
+
if commit:
|
|
269
|
+
self.raw_driver.commit()
|
|
270
|
+
|
|
271
|
+
self.submission_contract = self.get_contract("submission")
|
|
272
|
+
|
|
273
|
+
def flush(self):
|
|
274
|
+
# flushes storage and resubmits genesis contracts
|
|
275
|
+
self.raw_driver.flush_full()
|
|
276
|
+
self.raw_driver.flush_cache()
|
|
277
|
+
|
|
278
|
+
if self.submission_filename is not None:
|
|
279
|
+
self.set_submission_contract()
|
|
280
|
+
|
|
281
|
+
# Returns abstract contract which has partial methods mapped to each exported function.
|
|
282
|
+
def get_contract(self, name):
|
|
283
|
+
contract = self.raw_driver.get_contract(name)
|
|
284
|
+
|
|
285
|
+
if contract is None:
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
tree = ast.parse(contract)
|
|
289
|
+
|
|
290
|
+
function_defs = [
|
|
291
|
+
n for n in ast.walk(tree) if isinstance(n, ast.FunctionDef)
|
|
292
|
+
]
|
|
293
|
+
|
|
294
|
+
funcs = []
|
|
295
|
+
for definition in function_defs:
|
|
296
|
+
func_name = definition.name
|
|
297
|
+
kwargs = [arg.arg for arg in definition.args.args]
|
|
298
|
+
|
|
299
|
+
funcs.append((func_name, kwargs))
|
|
300
|
+
|
|
301
|
+
return AbstractContract(
|
|
302
|
+
name=name,
|
|
303
|
+
signer=self.signer,
|
|
304
|
+
environment=self.environment,
|
|
305
|
+
executor=self.executor,
|
|
306
|
+
funcs=funcs,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def closure_to_code_string(self, f):
|
|
310
|
+
closure_code = inspect.getsource(f)
|
|
311
|
+
closure_code = autopep8.fix_code(closure_code)
|
|
312
|
+
closure_tree = ast.parse(closure_code)
|
|
313
|
+
|
|
314
|
+
# Remove the enclosing function by swapping out the function def node with its children
|
|
315
|
+
assert len(closure_tree.body) == 1, "Module has multiple body nodes."
|
|
316
|
+
assert isinstance(closure_tree.body[0], ast.FunctionDef), (
|
|
317
|
+
"Function definition not found at root."
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
func_def_body = closure_tree.body[0]
|
|
321
|
+
closure_tree.body = func_def_body.body
|
|
322
|
+
|
|
323
|
+
contract_code = ast.unparse(closure_tree)
|
|
324
|
+
name = func_def_body.name
|
|
325
|
+
|
|
326
|
+
return contract_code, name
|
|
327
|
+
|
|
328
|
+
def lint(self, f, raise_errors=False):
|
|
329
|
+
if isinstance(f, FunctionType):
|
|
330
|
+
f, _ = self.closure_to_code_string(f)
|
|
331
|
+
|
|
332
|
+
if raise_errors:
|
|
333
|
+
self.compiler.linter.check_raise(f)
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
return self.compiler.linter.check(f)
|
|
337
|
+
|
|
338
|
+
def compile(self, f):
|
|
339
|
+
if isinstance(f, FunctionType):
|
|
340
|
+
f, _ = self.closure_to_code_string(f)
|
|
341
|
+
|
|
342
|
+
code = self.compiler.parse_to_code(f)
|
|
343
|
+
return code
|
|
344
|
+
|
|
345
|
+
def submit(
|
|
346
|
+
self,
|
|
347
|
+
f,
|
|
348
|
+
name=None,
|
|
349
|
+
metering=None,
|
|
350
|
+
owner=None,
|
|
351
|
+
constructor_args={},
|
|
352
|
+
signer=None,
|
|
353
|
+
):
|
|
354
|
+
|
|
355
|
+
assert self.submission_contract is not None, (
|
|
356
|
+
"No submission contract set. Try set_submission_contract first."
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
if isinstance(f, FunctionType):
|
|
360
|
+
f, n = self.closure_to_code_string(f)
|
|
361
|
+
if name is None:
|
|
362
|
+
name = n
|
|
363
|
+
|
|
364
|
+
assert name is not None, "No name provided."
|
|
365
|
+
|
|
366
|
+
if signer is None:
|
|
367
|
+
signer = self.signer
|
|
368
|
+
|
|
369
|
+
self.submission_contract.submit_contract(
|
|
370
|
+
name=name,
|
|
371
|
+
code=f,
|
|
372
|
+
owner=owner,
|
|
373
|
+
constructor_args=constructor_args,
|
|
374
|
+
metering=metering,
|
|
375
|
+
signer=signer,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
def get_contracts(self):
|
|
379
|
+
contracts = []
|
|
380
|
+
for key in self.raw_driver.keys():
|
|
381
|
+
if key.endswith(".__code__"):
|
|
382
|
+
contracts.append(key.replace(".__code__", ""))
|
|
383
|
+
return contracts
|
|
384
|
+
|
|
385
|
+
def get_var(self, contract, variable, arguments=[], mark=False):
|
|
386
|
+
return self.raw_driver.get_var(contract, variable, arguments, mark)
|
|
387
|
+
|
|
388
|
+
def set_var(self, contract, variable, arguments=[], value=None, mark=False):
|
|
389
|
+
self.raw_driver.set_var(contract, variable, arguments, value, mark)
|
|
File without changes
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
|
|
3
|
+
from contracting import constants
|
|
4
|
+
from contracting.compilation.linter import Linter, LintingError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ContractingCompiler(ast.NodeTransformer):
|
|
8
|
+
def __init__(self, module_name="__main__", linter=None):
|
|
9
|
+
self.module_name = module_name
|
|
10
|
+
self.linter = linter or Linter()
|
|
11
|
+
self.lint_alerts = None
|
|
12
|
+
self.source = None
|
|
13
|
+
self.constructor_visited = False
|
|
14
|
+
self.private_names = set()
|
|
15
|
+
self.orm_names = set()
|
|
16
|
+
self.visited_names = set() # store the method visits
|
|
17
|
+
|
|
18
|
+
def parse(self, source: str, lint=True):
|
|
19
|
+
self.constructor_visited = False
|
|
20
|
+
self.source = source
|
|
21
|
+
|
|
22
|
+
tree = ast.parse(source)
|
|
23
|
+
|
|
24
|
+
if lint:
|
|
25
|
+
self.lint_alerts = self.linter.check(tree)
|
|
26
|
+
else:
|
|
27
|
+
self.lint_alerts = None
|
|
28
|
+
|
|
29
|
+
tree = self.visit(tree)
|
|
30
|
+
|
|
31
|
+
if self.lint_alerts is not None:
|
|
32
|
+
raise LintingError(self.lint_alerts)
|
|
33
|
+
|
|
34
|
+
# check all visited nodes and see if they are actually private
|
|
35
|
+
|
|
36
|
+
# An Expr node can have a value func of compilation.Name, or compilation.
|
|
37
|
+
# Attribute which you much access the value of.
|
|
38
|
+
# TODO: This code branching is not ideal and should be investigated for simplicity.
|
|
39
|
+
for node in self.visited_names:
|
|
40
|
+
if node.id in self.private_names or node.id in self.orm_names:
|
|
41
|
+
node.id = self.privatize(node.id)
|
|
42
|
+
|
|
43
|
+
ast.fix_missing_locations(tree)
|
|
44
|
+
|
|
45
|
+
# reset state
|
|
46
|
+
self.private_names = set()
|
|
47
|
+
self.orm_names = set()
|
|
48
|
+
self.visited_names = set()
|
|
49
|
+
self.lint_alerts = None
|
|
50
|
+
self.source = None
|
|
51
|
+
|
|
52
|
+
return tree
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def privatize(s):
|
|
56
|
+
return "{}{}".format(constants.PRIVATE_METHOD_PREFIX, s)
|
|
57
|
+
|
|
58
|
+
def compile(self, source: str, lint=True):
|
|
59
|
+
tree = self.parse(source, lint=lint)
|
|
60
|
+
|
|
61
|
+
compiled_code = compile(tree, "<compilation>", "exec")
|
|
62
|
+
|
|
63
|
+
return compiled_code
|
|
64
|
+
|
|
65
|
+
def normalize_source(self, source: str, lint=True):
|
|
66
|
+
tree = ast.parse(source)
|
|
67
|
+
|
|
68
|
+
if lint:
|
|
69
|
+
lint_alerts = self.linter.check(tree)
|
|
70
|
+
if lint_alerts is not None:
|
|
71
|
+
raise LintingError(lint_alerts)
|
|
72
|
+
|
|
73
|
+
ast.fix_missing_locations(tree)
|
|
74
|
+
return ast.unparse(tree)
|
|
75
|
+
|
|
76
|
+
def parse_to_code(self, source, lint=True):
|
|
77
|
+
tree = self.parse(source, lint=lint)
|
|
78
|
+
return ast.unparse(tree)
|
|
79
|
+
|
|
80
|
+
def visit_FunctionDef(self, node):
|
|
81
|
+
|
|
82
|
+
# Presumes all decorators are valid, as caught by linter.
|
|
83
|
+
if node.decorator_list:
|
|
84
|
+
# Presumes that a single decorator is passed. This is caught by the linter.
|
|
85
|
+
decorator = node.decorator_list.pop()
|
|
86
|
+
|
|
87
|
+
# change the name of the init function to '____' so it is uncallable except once
|
|
88
|
+
if decorator.id == constants.INIT_DECORATOR_STRING:
|
|
89
|
+
node.name = "____"
|
|
90
|
+
|
|
91
|
+
elif decorator.id == constants.EXPORT_DECORATOR_STRING:
|
|
92
|
+
# Transform @export decorators to @__export(contract_name) decorators
|
|
93
|
+
decorator.id = "{}{}".format(
|
|
94
|
+
"__", constants.EXPORT_DECORATOR_STRING
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
new_node = ast.Call(
|
|
98
|
+
func=decorator,
|
|
99
|
+
args=[ast.Constant(value=self.module_name)],
|
|
100
|
+
keywords=[],
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
node.decorator_list.append(new_node)
|
|
104
|
+
|
|
105
|
+
else:
|
|
106
|
+
self.private_names.add(node.name)
|
|
107
|
+
node.name = self.privatize(node.name)
|
|
108
|
+
|
|
109
|
+
self.generic_visit(node)
|
|
110
|
+
|
|
111
|
+
return node
|
|
112
|
+
|
|
113
|
+
def visit_Assign(self, node):
|
|
114
|
+
if (
|
|
115
|
+
isinstance(node.value, ast.Call)
|
|
116
|
+
and not isinstance(node.value.func, ast.Attribute)
|
|
117
|
+
and node.value.func.id in constants.ORM_CLASS_NAMES
|
|
118
|
+
):
|
|
119
|
+
node.value.keywords.append(
|
|
120
|
+
ast.keyword(
|
|
121
|
+
arg="contract",
|
|
122
|
+
value=ast.Constant(value=self.module_name),
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
node.value.keywords.append(
|
|
126
|
+
ast.keyword(
|
|
127
|
+
arg="name",
|
|
128
|
+
value=ast.Constant(value=node.targets[0].id),
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
self.orm_names.add(node.targets[0].id)
|
|
132
|
+
|
|
133
|
+
self.generic_visit(node)
|
|
134
|
+
|
|
135
|
+
return node
|
|
136
|
+
|
|
137
|
+
def visit_Name(self, node):
|
|
138
|
+
self.visited_names.add(node)
|
|
139
|
+
return node
|
|
140
|
+
|
|
141
|
+
def visit_Expr(self, node):
|
|
142
|
+
self.generic_visit(node)
|
|
143
|
+
return node
|
|
144
|
+
|
|
145
|
+
def visit_Constant(self, node):
|
|
146
|
+
if isinstance(node.value, float):
|
|
147
|
+
literal = ast.get_source_segment(self.source, node) or str(
|
|
148
|
+
node.value
|
|
149
|
+
)
|
|
150
|
+
return ast.Call(
|
|
151
|
+
func=ast.Name(id="decimal", ctx=ast.Load()),
|
|
152
|
+
args=[ast.Constant(value=literal)],
|
|
153
|
+
keywords=[],
|
|
154
|
+
)
|
|
155
|
+
return node
|