Skip to content

Commit d7f7611

Browse files
authored
Comments and cleanup (#9)
* Added every comment for new simlib routines/functions * Fixed duplicated wrapped init calls * Changed compiled methods to use kwargs End user function still take in regular arguments -- only the internals have changed. Calling compiled functions now requires kwargs not args
1 parent 9099a1a commit d7f7611

File tree

16 files changed

+627
-295
lines changed

16 files changed

+627
-295
lines changed

ngcsimlib/commands/clamp.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from ngcsimlib.commands.command import Command
22
from ngcsimlib.utils import extract_args
3-
from ngcsimlib.componentUtils import find_compartment
4-
from ngcsimlib.logger import warn, error
3+
from ngcsimlib.logger import error
4+
from ngcsimlib.compartment import Compartment
55

66
class Clamp(Command):
77
"""
@@ -39,16 +39,14 @@ def __init__(self, components=None, compartment=None, clamp_name=None,
3939
self.compartment = compartment
4040

4141
for name, component in self.components.items():
42-
_, mapped_name = find_compartment(component.compartments, component.__class__, self.compartment)
42+
mapped = hasattr(component, self.compartment)
43+
if mapped:
44+
if Compartment.is_compartment(getattr(compartment, self.compartment)):
45+
continue
4346

44-
if mapped_name is None:
45-
error(self.name, " is attempting to initialize clamp to non-existent compartment \"",
46-
self.compartment, "\" on ", name, sep=" ")
47+
error(self.name, " is attempting to initialize clamp to non-existent compartment \"",
48+
self.compartment, "\" on ", name, sep=" ")
4749

48-
if mapped_name != self.compartment:
49-
warn(self.name, " is attempting to initialize clamp to a property name, not a compartment of ", name,
50-
". If using a verboseDict and autoMap this will work, otherwise this clamp will have unknown "
51-
"behavior", sep=" ")
5250

5351
def __call__(self, *args, **kwargs):
5452
try:

ngcsimlib/compartment.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,39 @@
44

55

66
class Compartment:
7+
"""
8+
Compartments in ngcsimlib are container objects for storing the stateful values of components. Compartments are
9+
tracked globaly and are automatically linked to components and methods during compiling to allow for stateful
10+
mechanics to be run without the need for the class object. Compartments also provide an entry and exit point for
11+
values inside of components allowing for cables to be connected for sending and receiving values.
12+
"""
13+
714
@classmethod
815
def is_compartment(cls, obj):
16+
"""
17+
A method for verifying if a provided object is a compartment. All compartments have `_is_compartment` set to
18+
true by default and this is
19+
20+
Args:
21+
obj: The object to check if it is a compartment
22+
23+
Returns:
24+
boolean if the provided object is a compartment
25+
"""
926
return hasattr(obj, "_is_compartment")
1027

1128
def __init__(self, initial_value=None, static=False):
29+
"""
30+
Builds a compartment to be used inside a component. It is important to note that building compartments
31+
outside of components may cause unexpected behavior as components interact with their compartments during
32+
construction to finish initializing them.
33+
Args:
34+
initial_value: The initial value of the compartment. As a general practice it is a good idea to
35+
provide a value that is similar to the values that will normally be stored here, such as an array of
36+
zeros of the correct length. (default: None)
1237
38+
static: a flag to lock a compartment to be static (default: False)
39+
"""
1340
self._is_compartment = True
1441
self.__add_connection = None
1542
self._static = static
@@ -19,27 +46,55 @@ def __init__(self, initial_value=None, static=False):
1946
self.path = None
2047

2148
def _setup(self, current_component, key):
49+
"""
50+
Finishes initializing the compartment, called by the component that builds the compartment
51+
(Handel automatically)
52+
"""
2253
self.__add_connection = current_component.add_connection
2354
self.name = current_component.name + "/" + key
2455
self.path = get_current_path() + "/" + self.name
2556
Set_Compartment_Batch({str(self.path): self})
2657

2758
def set(self, value):
59+
"""
60+
Sets the value of the compartment if it not static (Raises a runtime error)
61+
Args:
62+
value: the new value to be set
63+
"""
2864
if not self._static:
2965
self.value = value
3066
else:
3167
raise RuntimeError("Can not assign value to static compartment")
3268

3369
def clamp(self, value):
70+
"""
71+
A wrapper for the set command
72+
"""
3473
self.set(value)
3574

3675
def __repr__(self):
76+
"""
77+
Returns:
78+
returns the name of the compartment
79+
"""
3780
return f"[{self.name}]"
3881

3982
def __str__(self):
83+
"""
84+
Returns:
85+
returns the string representation of the value of the compartment
86+
"""
4087
return str(self.value)
4188

4289
def __lshift__(self, other) -> None:
90+
"""
91+
Overrides the left shift operation to be used for wiring compartments into one another
92+
if other is not an Operation it will create an overwrite operation with other as the argument,
93+
otherwise it will use the provided operation
94+
95+
Args:
96+
other: Either another component or an instance of BaseOp
97+
"""
4398
if isinstance(other, BaseOp):
4499
other.set_destination(self)
45100
self.__add_connection(other)

ngcsimlib/compilers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from .command_compiler import compile as compile_command, dynamic_compile, wrap_command
1+
from .command_compiler import compile_command, dynamic_compile, wrap_command
22
from .component_compiler import compile as compile_component
33
from .op_compiler import compile as compile_op

ngcsimlib/compilers/command_compiler.py

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,60 @@
1+
"""
2+
This is the file that contains the code to compile a given command on a model.
3+
4+
There are a few ways to compile the commands for a model, firstly if there is a command object already initialized
5+
that has a valid compile key and a list of components the base method of `compile_command(command)` can be used to produce
6+
the desired output. If no command object has been initialized then the `dynamic_compile(*components, compile_key=None)`
7+
can be used to produce the desired output without the need to first go through a command object. The output of either
8+
compile method will be the same.
9+
10+
The output produced by compiling a command will be two objects.
11+
12+
The first object produced by compiling a command is the compiled method itself. This method requires at least one
13+
positional argument and then any number of additional arguments. The first argument that is provided to the compiled
14+
method is a python dictionary that contains the state for all compartments this method will need to access,
15+
as discerning this can be a challenge it is normal to just pass it all compartments present on your model. The
16+
remaining list of arguments are all the run time arguments that the various compiled methods need to run properly.
17+
The return value of this compiled method is the final state of all compartments after running through the compiled
18+
command. Note here that the value on the compartments are not automatically updated and that will need to be done after.
19+
20+
The second object produced by compiling a command is the list of arguments that the compile command is expecting to
21+
be passed in alongside the initial state of all the compartments. It is a good habit to get into printing this list
22+
out after compiling as it can help catch typos present in the compiled methods that will not cause the compiling to
23+
fail but will produce unknown behavior.
24+
25+
There is a wrapper method offered in this file we recommend using to assist with the design patterned required by the
26+
compiled command. This is done with `wrap_command(command)`. This method will return another method that removes the
27+
need for creating the initial state of the compartments and setting all the compartment values after running. Arguments
28+
are still required to be passed in at run time.
29+
30+
"""
131
from ngcsimlib.compilers.component_compiler import parse as parse_component, compile as compile_component
232
from ngcsimlib.compilers.op_compiler import parse as parse_connection
333
from ngcsimlib.utils import Get_Compartment_Batch, Set_Compartment_Batch
4-
34+
from ngcsimlib.logger import critical
535

636
def _compile(compile_key, components):
37+
"""
38+
This is the top level compile method for commands. Note this does not actually require you to compile a
39+
specific command object as it works purely off the compile key provided to the method.
40+
The general process that this takes to compile down everything, is by producing an execution order that knows which
41+
methods that are going to be called and where the results of the method are supposed to be stored.
42+
43+
The execution order is as follows:
44+
45+
For each component in the provided array of components;
46+
| compile it with the provided key
47+
| resolve the outputs of the compiled function
48+
49+
Args:
50+
compile_key: The key that is being compiled (mapped to each function that has the @resolver decorator
51+
above it)
52+
53+
components: The list of components to compile for this function
54+
55+
Returns:
56+
Produces the two objects described at the top of this file
57+
"""
758
assert compile_key is not None
859
## for each component, get compartments, get output compartments
960
resolvers = {}
@@ -30,15 +81,20 @@ def _compile(compile_key, components):
3081
path = str(component.__dict__[comp].path)
3182
if path not in needed_comps:
3283
needed_comps.append(path)
33-
arg_order = needed_args + needed_comps
84+
3485
exc_order = []
3586
for c_name, component in components.items():
36-
exc_order.extend(compile_component(component, resolvers[c_name], arg_order))
87+
exc_order.extend(compile_component(component, resolvers[c_name]))
88+
89+
def compiled(compartment_values, **kwargs):
90+
for n in needed_args:
91+
if n not in kwargs:
92+
critical(f"Missing keyword argument \"{n}\" in compiled function."
93+
f"\tExpected keyword arguments {needed_args}")
3794

38-
def compiled(compartment_values, *cargs):
3995
for exc, outs, name in exc_order:
40-
_comps = [compartment_values[key] for key in needed_comps]
41-
vals = exc(*cargs, *_comps)
96+
_comps = {key: compartment_values[key] for key in needed_comps}
97+
vals = exc(**kwargs, **_comps)
4298
if len(outs) == 1:
4399
compartment_values[outs[0]] = vals
44100
elif len(outs) > 1:
@@ -49,16 +105,49 @@ def compiled(compartment_values, *cargs):
49105
return compiled, needed_args
50106

51107

52-
def compile(command):
108+
def compile_command(command):
109+
"""
110+
Compiles a given command object to the spec described at the top of this file
111+
112+
Args:
113+
command: the command object
114+
115+
Returns:
116+
compiled_command, needed_arguments
117+
118+
"""
53119
return _compile(command.compile_key, command.components)
54120

55121

56122
def dynamic_compile(*components, compile_key=None):
123+
"""
124+
Dynamically compiles a command without the need of a command object to produce
125+
the spec described at the top of this file.
126+
127+
Args:
128+
*components: a list of components to be compiled
129+
130+
compile_key: the compile key specifying what to compile
131+
132+
Returns:
133+
compiled_command, needed_arguments
134+
"""
57135
assert compile_key is not None
58136
return _compile(compile_key, {c.name: c for c in components})
59137

60138

61139
def wrap_command(command):
140+
"""
141+
Wraps the provided command to provide the state of all compartments as input
142+
and saves the returned state to all compartments after running. Designed to
143+
be used with compiled commands
144+
145+
Args:
146+
command: the command to wrap
147+
148+
Returns:
149+
the output of the command after it's been executed
150+
"""
62151
def _wrapped(*args):
63152
vals = command(Get_Compartment_Batch(), *args)
64153
Set_Compartment_Batch(vals)
Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
1-
from ngcsimlib.compilers.op_compiler import compile as op_compile
1+
"""
2+
This is the file that contains the code to compile a component for a command.
3+
4+
There are two primary method provided in this file. The first of them is the parse command. This command is designed
5+
to provide everything that is needed to compile the component down without actually doing so. This is generally used
6+
by the command compiler to produce a working list of everything that is needed prior to the actual compiling of all
7+
components.
28
9+
The second method that is provided in this file the actual method for compiling the component. This method takes in
10+
the component, the parsed component, and the global argument order for the compiled method. The result of this method
11+
is the execution order needed to be run to compute the compiled method over this component. This execution order is
12+
consistent with the same pattern used by the command compiler.
13+
14+
"""
15+
from ngcsimlib.compilers.op_compiler import compile as op_compile
316
from ngcsimlib.utils import get_resolver
417
from ngcsimlib.compartment import Compartment
518

619

720
def parse(component, compile_key):
21+
"""
22+
Returns the parsed version of a component for use in compiling
23+
24+
Args:
25+
component: the component to parse
26+
27+
compile_key: the key to parse with
28+
29+
Returns: the pure function,
30+
the output compartments to resolve to,
31+
the arguments needed,
32+
the parameters needed,
33+
the compartments needed
34+
35+
"""
836
(pure_fn, output_compartments), (args, parameters, compartments, parse_varnames) = \
937
get_resolver(component.__class__.__name__, compile_key)
1038

@@ -28,45 +56,36 @@ def parse(component, compile_key):
2856
return (pure_fn, output_compartments, args, parameters, compartments)
2957

3058

31-
def compile(component, resolver, arg_order):
59+
def compile(component, resolver):
60+
"""
61+
compiles down the component to a single pure method
62+
63+
Args:
64+
component: the component to compile
65+
66+
resolver: the parsed output of the component
67+
68+
Returns:
69+
the compiled method
70+
"""
3271
exc_order = []
3372
pure_fn, outs, _args, params, comps = resolver
3473

3574
### Op resolve
3675
for connection in component.connections:
37-
exc_order.append(op_compile(connection, arg_order))
76+
exc_order.append(op_compile(connection))
3877

3978
### Component resolve
4079
comp_ids = [str(component.__dict__[comp].path) for _, comp in comps]
4180
out_ids = [str(component.__dict__[comp].path) for comp in outs]
4281

43-
funParams = [component.__dict__[narg] for _, narg in (list(params))]
44-
45-
arg_locs = [loc for loc, _ in _args]
46-
param_locs = [loc for loc, _ in params]
47-
comp_locs = [loc for loc, _ in comps]
82+
funParams = {narg: component.__dict__[narg] for _, narg in (list(params))}
4883

49-
def compiled(*args):
50-
funArgs = [args[arg_order.index(narg)] for _, narg in (list(_args))]
51-
funComps = [args[arg_order.index(narg)] for narg in comp_ids]
52-
53-
_arg_loc = 0
54-
_param_loc = 0
55-
_comps_loc = 0
56-
57-
fargs = []
58-
for i in range(len(arg_locs) + len(param_locs) + len(comp_locs)):
59-
if i in arg_locs:
60-
fargs.append(funArgs[_arg_loc])
61-
_arg_loc += 1
62-
elif i in param_locs:
63-
fargs.append(funParams[_param_loc])
64-
_param_loc += 1
65-
else:
66-
fargs.append(funComps[_comps_loc])
67-
_comps_loc += 1
84+
def compiled(**kwargs):
85+
funArgs = {narg: kwargs.get(narg) for _, narg in (list(_args))}
86+
funComps = {narg.split('/')[-1]: kwargs.get(narg) for narg in comp_ids}
6887

69-
return pure_fn(*fargs)
88+
return pure_fn(**funParams, **funArgs, **funComps)
7089

7190
exc_order.append((compiled, out_ids, component.name))
7291
return exc_order

0 commit comments

Comments
 (0)