first commit

This commit is contained in:
2020-11-03 18:30:14 -08:00
commit 31d8522470
1881 changed files with 345408 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
# Import python libs
import inspect
# Import pop libs
import pop.exc
import pop.hub
import pop.loader
from typing import Any, Callable, Dict, Iterable, List
def contract(
hub: "pop.hub.Hub", # pylint: disable=unused-argument
raws: Iterable["pop.loader.LoadedMod"],
mod: "pop.loader.LoadedMod",
):
"""
Verify module level contract - functions only
:param hub: The redistributed pop central hub
:param raws: A list of loaded modules with contracts
:param mod: A loader module
"""
sig_errs = []
sig_miss = []
mname = mod.__name__
for raw in raws:
if isinstance(raw, pop.loader.LoadError):
sig_errs.append(str(raw))
continue
else:
for fun in raw._funcs:
if fun.startswith("sig_"):
tfun = fun[4:]
if tfun not in mod._funcs:
sig_miss.append(tfun)
continue
sig_errs.extend(sig(mod._funcs[tfun].func, raw._funcs[fun].func))
if sig_errs or sig_miss:
msg = ""
if sig_errs:
msg += f"Signature Errors in {mname}:\n"
for err in sig_errs:
msg += f"{err}\n"
if sig_miss:
msg += f"Signature Functions Missing in {mname}:\n"
for err in sig_miss:
msg += f"{err}\n"
msg = msg.strip()
raise pop.exc.ContractSigException(msg)
def sig_map(ver: Callable) -> Dict[str, Any]:
"""
Generates the map dict for the signature verification
"""
vsig = inspect.signature(ver)
vparams = list(vsig.parameters.values())
vdat = {"args": [], "v_pos": -1, "kw": [], "kwargs": False, "ann": {}}
for ind in range(len(vparams)):
param = vparams[ind]
val = param.kind.value
name = param.name
if val == 0 or val == 1:
vdat["args"].append(name)
if param.default != inspect._empty: # Is a KW, can be inside of **kwargs
vdat["kw"].append(name)
elif val == 2:
vdat["v_pos"] = ind
elif val == 3:
vdat["kw"].append(name)
elif val == 4:
vdat["kwargs"] = ind
if param.annotation != inspect._empty:
vdat["ann"][name] = param.annotation
return vdat
def sig(func: Callable, ver: Callable) -> List[str]:
"""
Takes 2 functions, the first function is verified to have a parameter signature
compatible with the second function
"""
errors = []
fsig = inspect.signature(func)
fparams = list(fsig.parameters.values())
vdat = sig_map(ver)
arg_len = len(vdat["args"])
v_pos = False
for ind in range(len(fparams)):
param = fparams[ind]
val = param.kind.value
name = param.name
has_default = param.default != inspect._empty
ann = param.annotation
vann = vdat["ann"].get(name, inspect._empty)
if vann != ann:
errors.append(f'Parameter, "{name}" is type "{str(ann)}" not "{str(vann)}"')
if val == 2:
v_pos = True
if val == 0 or val == 1:
if ind >= arg_len: # Past available positional args
if not vdat["v_pos"] == -1: # Has a *args
if ind >= vdat["v_pos"] and v_pos:
# Invalid unless it is a kw
if not name in vdat["kw"]:
# Is a kw
errors.append(f'Parameter "{name}" is invalid')
if vdat["kwargs"] is False:
errors.append(f'Parameter "{name}" not defined as kw only')
continue
elif vdat["kwargs"] is not False and not has_default:
errors.append(
f'Parameter "{name}" is past available positional params'
)
elif vdat["kwargs"] is False:
errors.append(
f'Parameter "{name}" is past available positional params'
)
else:
v_param = vdat["args"][ind]
if v_param != name:
errors.append(
f'Parameter "{name}" does not have the correct name: {v_param}'
)
if val == 2:
if ind < vdat["v_pos"]:
errors.append(
f'Parameter "{name}" is not in the correct position for *args'
)
if val == 3:
if name not in vdat["kw"] and not vdat["kwargs"]:
errors.append(f'Parameter "{name}" is not available as a kwarg')
if val == 4:
if vdat["kwargs"] is False:
errors.append(f"Kwargs are not permitted as a parameter")
return errors