jaseci 1.4.0.19__py3-none-any.whl → 1.4.0.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jaseci might be problematic. Click here for more details.

jaseci/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.0.19
1
+ 1.4.0.20
jaseci/cli_tools/jsctl.py CHANGED
@@ -17,6 +17,7 @@ from jaseci.utils.utils import copy_func
17
17
  from .book_tools import Book, modifiedBook
18
18
  from jaseci.utils.utils import logger, perf_test_start, perf_test_stop, find_first_api
19
19
  from jaseci.jsorc.jsorc import JsOrc
20
+ from prettytable import PrettyTable
20
21
 
21
22
  session = None
22
23
 
@@ -112,6 +113,39 @@ def resolve_none_type(kwargs):
112
113
  kwargs[i] = None
113
114
 
114
115
 
116
+ def has_profile(output):
117
+ if isinstance(output, dict):
118
+ if "profile" in output.keys():
119
+ if "jac" in output["profile"].keys() and "perf" in output["profile"].keys():
120
+ return True
121
+ return False
122
+
123
+
124
+ def gen_pretty_table(csv_str):
125
+ rows = csv_str.split("\n")
126
+ row_width = len(rows[0].split(","))
127
+ first_row = rows[0].split(",")
128
+ if first_row[2] == "percall":
129
+ first_row[2] = "percall_tot"
130
+ try:
131
+ table = PrettyTable(first_row)
132
+ for i in rows[1:]:
133
+ row = i.split(",")
134
+ if len(row) != row_width:
135
+ continue
136
+ table.add_row(row)
137
+ return table
138
+ except Exception as e:
139
+ click.echo(f"Something went wrong pretty printing profile: {e}")
140
+
141
+
142
+ def pretty_profile(output):
143
+ click.echo("Jac Code Profile:")
144
+ click.echo(gen_pretty_table(output["profile"]["jac"]))
145
+ click.echo("\nInternal Jaseci Profile:")
146
+ click.echo(gen_pretty_table(output["profile"]["perf"]))
147
+
148
+
115
149
  def interface_api(api_name, is_public, is_cli_only, **kwargs):
116
150
  """
117
151
  Interfaces Master apis after processing arguments/parameters
@@ -137,6 +171,7 @@ def interface_api(api_name, is_public, is_cli_only, **kwargs):
137
171
  out = session["master"].public_interface_to_api(kwargs, api_name)
138
172
  else:
139
173
  out = session["master"].general_interface_to_api(kwargs, api_name)
174
+ d_out = out
140
175
  if (
141
176
  isinstance(out, dict)
142
177
  and "report_custom" in out.keys()
@@ -153,6 +188,8 @@ def interface_api(api_name, is_public, is_cli_only, **kwargs):
153
188
  if not session["mem-only"]:
154
189
  with open(session["filename"], "wb") as f:
155
190
  pickle.dump(session, f)
191
+ if has_profile(d_out):
192
+ pretty_profile(d_out)
156
193
 
157
194
 
158
195
  def extract_api_tree():
@@ -384,20 +421,22 @@ def script(filename, profile, output):
384
421
  with open(output, "w") as f:
385
422
  f.write("Multi Command Script Output:\n")
386
423
  for i in cmds:
387
- res = CliRunner(mix_stderr=False).invoke(jsctl, i.split())
424
+ res = CliRunner(mix_stderr=False).invoke(jsctl, i)
388
425
  click.echo(res.stdout)
389
426
  if output:
390
427
  with open(output, "a") as f:
391
428
  f.write(f"Output for {i}:\n")
392
429
  f.write(res.stdout)
393
430
  if profile:
394
- perf = perf_test_stop(prof)
431
+ perf, graph = perf_test_stop(prof)
395
432
  click.echo(perf)
433
+ click.echo(graph)
396
434
  if output:
397
435
  with open(output, "a") as f:
398
436
  if profile:
399
437
  f.write(f"\nProfile:\n")
400
438
  f.write(perf)
439
+ f.write(graph)
401
440
  click.echo(f"[saved to {output}]")
402
441
 
403
442
 
@@ -506,6 +506,17 @@ class JsctlTest(TestCaseHelper, TestCase):
506
506
  ],
507
507
  )
508
508
 
509
+ def test_jsctl_pretty_profiles(self):
510
+ self.call(f"actions load local {self.infer_loc}")
511
+ r = self.call(
512
+ f"sentinel register "
513
+ f"{os.path.dirname(__file__)}/graph_can.jac -name gc -set_active true"
514
+ )
515
+ r = self.call("walker run go -profiling true")
516
+ self.assertIn("walker::go", r)
517
+ self.assertIn("cumtime", r)
518
+ self.assertIn("cum_time", r)
519
+
509
520
 
510
521
  class JsctlTestWithSession(TestCaseHelper, TestCase):
511
522
  """Unit tests for Jac language"""
@@ -14,4 +14,5 @@ def start_perf_test(name: str = "default"):
14
14
  def stop_perf_test(name: str = "default"):
15
15
  if name not in perf_tests.keys():
16
16
  return
17
- return perf_test_stop(perf_tests[name])
17
+ calls, graph = perf_test_stop(perf_tests[name])
18
+ return {"calls": calls, "graph": graph}
@@ -11,6 +11,7 @@ from jaseci.extens.svc.elastic_svc import Elastic
11
11
 
12
12
  import sys
13
13
  import json
14
+ import time
14
15
 
15
16
 
16
17
  @jaseci_action()
@@ -49,6 +50,12 @@ def err(*args):
49
50
  print(*args, file=sys.stderr)
50
51
 
51
52
 
53
+ @jaseci_action()
54
+ def sleep(secs: float):
55
+ """Standard built in for sleep"""
56
+ return time.sleep(secs)
57
+
58
+
52
59
  @jaseci_action()
53
60
  def sort_by_col(lst: list, col_num: int, reverse: bool = False):
54
61
  """
@@ -21,7 +21,9 @@ class StdLibTest(CoreTest):
21
21
  ret = self.call(self.mast, ["walker_run", {"name": "internal_lib"}])
22
22
  self.assertEqual(len(ret["report"]), 1)
23
23
  self.assertTrue(
24
- ret["report"][0].startswith("ncalls,tottime,percall,cumtime,percall,")
24
+ ret["report"][0]["calls"].startswith(
25
+ "ncalls,tottime,percall,cumtime,percall,"
26
+ )
25
27
  )
26
28
 
27
29
  def test_rand_float_round(self):
@@ -88,6 +88,37 @@ class JsOrcApi:
88
88
 
89
89
  return new_config
90
90
 
91
+ @Interface.admin_api()
92
+ def jsorc_refresh(self):
93
+ """
94
+ refreshing jsorc's config.
95
+ """
96
+
97
+ JsOrc.configure()
98
+
99
+ return {
100
+ "running_interval": JsOrc._running_interval,
101
+ "config": JsOrc._config,
102
+ }
103
+
104
+ @Interface.admin_api(cli_args=["name"])
105
+ def service_info(self, name: str):
106
+ """
107
+ getting service's info.
108
+ """
109
+
110
+ # will throw exception if not existing
111
+ svc = JsOrc.svc(name)
112
+
113
+ return {
114
+ "enabled": svc.enabled,
115
+ "automated": svc.automated,
116
+ "quiet": svc.quiet,
117
+ "state": svc.state.name,
118
+ "config": svc.config,
119
+ "error": str(svc.error) if svc.error else None,
120
+ }
121
+
91
122
  @Interface.admin_api(cli_args=["name"])
92
123
  def service_refresh(self, name: str):
93
124
  """
@@ -22,6 +22,11 @@ class WalkerApi:
22
22
  def __init__(self):
23
23
  self.spawned_walker_ids = IdList(self)
24
24
  self.yielded_walkers_ids = IdList(self)
25
+ self.reset_profiling(False)
26
+
27
+ def reset_profiling(self, profiling):
28
+ self._profiling = profiling
29
+ self._jac_profile = {}
25
30
 
26
31
  @Interface.private_api(cli_args=["wlk"])
27
32
  def walker_get(self, wlk: Walker, mode: str = "default", detailed: bool = False):
@@ -166,6 +171,7 @@ class WalkerApi:
166
171
  """
167
172
  Executes walker (assumes walker is primed)
168
173
  """
174
+ self.reset_profiling(profiling)
169
175
  return wlk.run(
170
176
  start_node=prime, prime_ctx=ctx, request_ctx=_req_ctx, profiling=profiling
171
177
  )
@@ -200,6 +206,7 @@ class WalkerApi:
200
206
  Creates walker instance, primes walker on node, executes walker,
201
207
  reports results, and cleans up walker instance.
202
208
  """
209
+ self.reset_profiling(profiling)
203
210
  wlk = self.yielded_walkers_ids.get_obj_by_name(name, silent=True)
204
211
  if wlk is None:
205
212
  wlk = snt.run_architype(
@@ -226,6 +233,7 @@ class WalkerApi:
226
233
  """
227
234
  Walker individual APIs
228
235
  """
236
+ self.reset_profiling(profiling)
229
237
  return self.walker_run(name, nd, ctx, _req_ctx, snt, profiling)
230
238
 
231
239
  @Interface.public_api(cli_args=["wlk"])
@@ -44,7 +44,7 @@ class ArchitypeInterp(Interp):
44
44
 
45
45
  kid = self.set_cur_ast(jac_ast)
46
46
 
47
- self.push_scope(JacScope(parent=self))
47
+ self.push_scope(JacScope(parent=self, name=f"spawn:{jac_ast.loc_str()}"))
48
48
  if kid[0].name == "KW_NODE":
49
49
  item = Node(
50
50
  m_id=self._m_id,
@@ -173,14 +173,11 @@ class ArchitypeInterp(Interp):
173
173
  """
174
174
  kid = self.set_cur_ast(jac_ast)
175
175
  root_name = self.run_has_root(kid[1])
176
- m = Interp(parent_override=self.parent(), caller=self)
177
- m.push_scope(JacScope(parent=self, here=self))
178
176
  try:
179
- m.run_code_block(kid[4])
177
+ self.run_code_block(kid[4])
180
178
  except Exception as e:
181
- self.rt_error(f"Internal Exception: {e}", m._cur_jac_ast)
182
- local_state = m._jac_scope.local_scope
183
- self.report = self.report + m.report
179
+ self.rt_error(f"Internal Exception: {e}", self._cur_jac_ast)
180
+ local_state = self._jac_scope.local_scope
184
181
  if root_name in local_state.keys():
185
182
  obj = local_state[root_name]
186
183
  if not isinstance(obj, Node):
@@ -125,7 +125,9 @@ class SentinelInterp(Interp):
125
125
  def arch_can_compile(self, jac_ast, arch):
126
126
  """Helper function to statically compile can stmts for arch"""
127
127
  kid = self.set_cur_ast(jac_ast)
128
- self.push_scope(JacScope(parent=self, has_obj=None))
128
+ self.push_scope(
129
+ JacScope(parent=self, name=f"a_cgen:{jac_ast.loc_str()}", has_obj=None)
130
+ )
129
131
  if jac_ast.name in ["attr_block", "walker_block"]:
130
132
  for i in kid:
131
133
  if i.name == "attr_stmt" and i.kid[0].name == "can_stmt":
@@ -178,6 +180,7 @@ class SentinelInterp(Interp):
178
180
  m_id=self._m_id,
179
181
  h=self._h,
180
182
  name=action_name,
183
+ kind="ability",
181
184
  code_ir=ir,
182
185
  preset_in_out=preset_in_out,
183
186
  access_list=access_list,
@@ -24,6 +24,7 @@ class WalkerInterp(Interp):
24
24
  self.scope_and_run(
25
25
  jac_ast if jac_ast.name == "walker_block" else kid[-1],
26
26
  self.run_walker_block,
27
+ scope_name=f"w_run:{jac_ast.loc_str()}",
27
28
  )
28
29
 
29
30
  def run_walker_block(self, jac_ast):
@@ -188,10 +189,10 @@ class WalkerInterp(Interp):
188
189
  """
189
190
  kid = self.set_cur_ast(jac_ast)
190
191
  param_list = {"args": [], "kwargs": []}
191
- m = Interp(parent_override=self.parent(), caller=self)
192
- m.push_scope(
192
+ self.push_scope(
193
193
  JacScope(
194
194
  parent=self,
195
+ name=f"p_in_out:{jac_ast.loc_str()}",
195
196
  has_obj=self.current_node,
196
197
  here=self.current_node,
197
198
  visitor=self,
@@ -199,17 +200,18 @@ class WalkerInterp(Interp):
199
200
  )
200
201
 
201
202
  if kid[1].name == "param_list":
202
- param_list = m.run_param_list(kid[1]).value
203
+ param_list = self.run_param_list(kid[1]).value
203
204
  try:
204
205
  result = act.run_action(param_list, self._jac_scope, self)
205
206
  except Exception as e:
206
- self.rt_error(f"Internal Exception: {e}", m._cur_jac_ast)
207
+ self.rt_error(f"Internal Exception: {e}", self._cur_jac_ast)
207
208
  result = None
208
209
  if kid[-1].name == "expression":
209
- m.run_expression(kid[-1])
210
- dest = m.pop()
210
+ self.run_expression(kid[-1])
211
+ dest = self.pop()
211
212
  dest.value = result
212
213
  dest.write(kid[-1])
214
+ self.pop_scope()
213
215
 
214
216
  # Helper Functions ##################
215
217
  def auto_trigger_node_actions(self, act_list):
@@ -229,13 +231,19 @@ class WalkerInterp(Interp):
229
231
  if not i.preset_in_out: # All preset in and outs get executed
230
232
  already_executed.append(i.name)
231
233
 
232
- def scope_and_run(self, jac_ast, run_func):
234
+ def scope_and_run(self, jac_ast, run_func, scope_name):
233
235
  """
234
236
  Helper to run ast elements with execution scope added
235
237
  (Useful for running arbitrary code blocks as one-offs)
236
238
  """
237
239
  self.push_scope(
238
- JacScope(parent=self, has_obj=self, here=self.current_node, visitor=self)
240
+ JacScope(
241
+ parent=self,
242
+ name=scope_name,
243
+ has_obj=self,
244
+ here=self.current_node,
245
+ visitor=self,
246
+ )
239
247
  )
240
248
  run_func(jac_ast)
241
249
  self.pop_scope()
jaseci/jac/ir/ast.py CHANGED
@@ -16,7 +16,12 @@ class Ast:
16
16
  ):
17
17
  self.name = "unparsed"
18
18
  self.kid = []
19
- self.loc = [0, 0, mod_name if mod_name is not None else "@default", {}]
19
+ self.loc = [
20
+ 0,
21
+ 0,
22
+ mod_name if mod_name is not None else "@default",
23
+ {},
24
+ ] # line, col, module, tokens
20
25
 
21
26
  def is_terminal(self):
22
27
  """Returns true if node is a terminal"""
@@ -37,6 +42,9 @@ class Ast:
37
42
  if self.is_terminal():
38
43
  return self.token()["symbol"]
39
44
 
45
+ def loc_str(self):
46
+ return f"{self.loc[2]}:{self.loc[0]}"
47
+
40
48
  def __str__(self):
41
49
  res = f"{self.name}:{self.loc[2]}:{self.loc[0]}:{self.loc[1]}:"
42
50
  if self.is_terminal():
@@ -8,12 +8,16 @@ from jaseci.jsorc.live_actions import get_global_actions
8
8
 
9
9
 
10
10
  class JacScope:
11
- def __init__(self, parent, has_obj=None, here=None, visitor=None):
11
+ def __init__(self, parent, name, has_obj=None, here=None, visitor=None):
12
12
  self.parent = parent
13
+ self.name = name
13
14
  self.local_scope = {}
14
15
  self.has_obj = has_obj if has_obj else self
15
16
  self.context = {}
16
17
  self.action_sets = []
18
+ self._start_time = 0 # For profiling
19
+ self._total_time = 0 # For profiling
20
+ self._cum_start_time = None
17
21
  self.set_refs(here, visitor)
18
22
  self.setup_actions()
19
23
 
@@ -19,12 +19,16 @@ from jaseci.prim.edge import Edge
19
19
  from jaseci.prim.node import Node
20
20
  from jaseci.jac.machine.jac_value import JacValue
21
21
  from jaseci.jac.jsci_vm.op_codes import JsCmp
22
+ import time
22
23
 
23
24
 
24
25
  class MachineState:
25
26
  """Shared interpreter class across both sentinels and walkers"""
26
27
 
27
- def __init__(self, parent_override=None, caller=None):
28
+ recur_detect_set = []
29
+ profile_stack = [None]
30
+
31
+ def __init__(self):
28
32
  self.report = []
29
33
  self.report_status = None
30
34
  self.report_custom = None
@@ -32,10 +36,6 @@ class MachineState:
32
36
  self.runtime_errors = []
33
37
  self.yielded_walkers_ids = IdList(self)
34
38
  self.ignore_node_ids = IdList(self)
35
- self._parent_override = parent_override
36
- if not isinstance(self, Element) and caller:
37
- self._m_id = caller._m_id
38
- self._h = caller._h
39
39
  self._scope_stack = [None]
40
40
  self._jac_scope = None
41
41
  self._relevant_edges = []
@@ -46,18 +46,14 @@ class MachineState:
46
46
  self._loop_limit = 10000
47
47
  self._cur_jac_ast = Ast("none")
48
48
  self._write_candidate = None
49
+ self._mast = self.get_master()
50
+
49
51
  self.inform_hook()
50
52
 
51
53
  def inform_hook(self):
52
54
  if hasattr(self, "_h"):
53
55
  self._h._machine = self
54
56
 
55
- def parent(self): # parent here is always a sentinel
56
- if self._parent_override:
57
- return self._parent_override
58
- else:
59
- return Element.parent(self)
60
-
61
57
  def reset(self):
62
58
  self.report = []
63
59
  self.report_status = None
@@ -69,12 +65,74 @@ class MachineState:
69
65
  self._stopped = None
70
66
 
71
67
  def push_scope(self, scope: JacScope):
68
+ self.profile_pause()
72
69
  self._scope_stack.append(scope)
73
70
  self._jac_scope = scope
71
+ MachineState.profile_stack.append(self._jac_scope)
72
+ self.profile_in()
73
+ MachineState.recur_detect_set.append(self.call_name())
74
74
 
75
75
  def pop_scope(self):
76
+ MachineState.recur_detect_set.remove(self.call_name())
77
+ self.profile_out()
76
78
  self._scope_stack.pop()
77
79
  self._jac_scope = self._scope_stack[-1]
80
+ MachineState.profile_stack.pop()
81
+ self.profile_unpause()
82
+
83
+ def profile_in(self):
84
+ if self._mast and self._mast._profiling:
85
+ self._jac_scope._start_time = time.time()
86
+ self._jac_scope._cum_start_time = time.time()
87
+
88
+ def profile_out(self):
89
+ # profile_jac_scope = MachineState.profile_stack[-1] # refactor and clean
90
+ if self._mast and self._mast._profiling:
91
+ name = self.call_name()
92
+ if name not in self._mast._jac_profile:
93
+ self._mast._jac_profile[name] = {
94
+ "calls": 1,
95
+ "u_calls": 0 if name in MachineState.recur_detect_set else 1,
96
+ "tot_time": self._jac_scope._total_time
97
+ + (time.time() - self._jac_scope._start_time),
98
+ "cum_time": 0
99
+ if name in MachineState.recur_detect_set
100
+ else time.time() - self._jac_scope._cum_start_time,
101
+ }
102
+ else:
103
+ c = self._mast._jac_profile[name]["calls"]
104
+ u = self._mast._jac_profile[name]["u_calls"]
105
+ t = self._mast._jac_profile[name]["tot_time"]
106
+ p = self._mast._jac_profile[name]["cum_time"]
107
+ self._mast._jac_profile[name]["calls"] = c + 1
108
+ self._mast._jac_profile[name]["u_calls"] = (
109
+ u if name in MachineState.recur_detect_set else u + 1
110
+ )
111
+ self._mast._jac_profile[name][
112
+ "tot_time"
113
+ ] += self._jac_scope._total_time + (
114
+ time.time() - self._jac_scope._start_time
115
+ )
116
+
117
+ self._mast._jac_profile[name]["cum_time"] = (
118
+ p
119
+ if name in MachineState.recur_detect_set
120
+ else (p + time.time() - self._jac_scope._cum_start_time)
121
+ )
122
+
123
+ def call_name(self):
124
+ return f"{self.kind}::{self.name}:{self._jac_scope.name}"
125
+
126
+ def profile_pause(self):
127
+ _jac_scope = MachineState.profile_stack[-1] # refactor and clean
128
+ if self._mast and self._mast._profiling and _jac_scope:
129
+ _jac_scope._total_time += time.time() - _jac_scope._start_time
130
+ _jac_scope._start_time = 0
131
+
132
+ def profile_unpause(self):
133
+ _jac_scope = MachineState.profile_stack[-1] # refactor and clean
134
+ if self._mast and self._mast._profiling and _jac_scope:
135
+ _jac_scope._start_time = time.time()
78
136
 
79
137
  def here(self):
80
138
  return self._scope_stack[-1].here() if self._scope_stack[-1] else None