Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions nbs/00_core.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@
" def _wrap(v):\n",
" if allow_policy is None or v is ... or isinstance(v, tuple): return v\n",
" return (v, allow_policy)\n",
" added = []\n",
" def _add(k, *vs):\n",
" new = [v for v in vs if v not in __pytools__[k]]\n",
" __pytools__[k].update(new)\n",
" added.extend((k,v) for v in new)\n",
" res = None\n",
" for o in c:\n",
" if hasattr(o, '__allow__'):\n",
Expand All @@ -360,18 +365,18 @@
" allow(k, allow_policy=v)\n",
" continue\n",
" vals = listify(v)\n",
" __pytools__[k].update(_wrap(x) for x in vals)\n",
" _add(k, *map(_wrap, vals))\n",
" for x in vals:\n",
" if isinstance(name := x[0] if isinstance(x, tuple) else x, str): _set_wrapped(k, name)\n",
" else:\n",
" res = track_call(o) if callable(o) else o\n",
" if not isinstance(o, type) and callable(o) and not inspect.isroutine(o) and not hasattr(o, '__qualname__'):\n",
" __pytools__[o].add(_wrap('__call__'))\n",
" _add(o, _wrap('__call__'))\n",
" _set_wrapped(type(o), '__call__')\n",
" continue\n",
" objclass = getattr(o, '__objclass__', None)\n",
" if objclass is not None:\n",
" __pytools__[objclass].add(_wrap(o.__name__))\n",
" _add(objclass, _wrap(o.__name__))\n",
" _set_wrapped(objclass, o.__name__, o)\n",
" continue\n",
" qualname = getattr(o, '__qualname__', '') or ''\n",
Expand All @@ -381,11 +386,12 @@
" if '.' in qualname:\n",
" cls = getattr(mod, qualname.rsplit('.', 1)[0], None)\n",
" if cls is not None:\n",
" __pytools__[cls].add(_wrap(o.__name__))\n",
" _add(cls, _wrap(o.__name__))\n",
" _set_wrapped(cls, o.__name__, o)\n",
" continue\n",
" __pytools__[mod].add(_wrap(o.__name__))\n",
" _add(mod, _wrap(o.__name__))\n",
" _set_wrapped(mod, o.__name__, o)\n",
" if added: sys.audit('pyskills.allowed', added)\n",
" if len(c)==1 and callable(c[0]): return res"
]
},
Expand Down Expand Up @@ -506,7 +512,7 @@
"id": "8658a494",
"metadata": {},
"source": [
"`allow` raises a `pyskills.allow` audit event before registering anything. Outside a sandbox that's a no-op; inside safepyrun's audit context it means sandboxed code can't broaden its own permissions by calling `allow`, since the event is denied like any other unapproved operation:"
"`allow` raises a `pyskills.allow` audit event before registering anything. Once it has added something, it raises a `pyskills.allowed` event carrying the `(key, entry)` pairs it added. Outside a sandbox both are no-ops. Inside safepyrun's audit context, the first means sandboxed code can't broaden its own permissions by calling `allow`, since the event is denied like any other unapproved operation. The second lets a host that did approve the call, such as for an import-allowed package registering its tools while it initializes, record exactly what changed:"
]
},
{
Expand All @@ -517,10 +523,11 @@
"outputs": [],
"source": [
"seen = []\n",
"sys.addaudithook(lambda ev,args: seen.append(args) if ev=='pyskills.allow' else None)\n",
"sys.addaudithook(lambda ev,args: seen.append((ev,args)) if ev.startswith('pyskills.allow') else None)\n",
"allow(_test_fn)\n",
"test_eq(seen[-1], ((_test_fn,),))\n",
"__pytools__[sys.modules['__main__']].discard('my_test_func')"
"__pytools__[sys.modules['__main__']].discard('my_test_func')\n",
"test_eq(seen[-2:], [('pyskills.allow', ((_test_fn,),)), ('pyskills.allowed', ([(sys.modules['__main__'], 'my_test_func')],))])\n",
"seen[-1]"
]
},
{
Expand Down
16 changes: 11 additions & 5 deletions pyskills/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ def allow(*c, allow_policy=None): # Callable that raises if call not allowed
def _wrap(v):
if allow_policy is None or v is ... or isinstance(v, tuple): return v
return (v, allow_policy)
added = []
def _add(k, *vs):
new = [v for v in vs if v not in __pytools__[k]]
__pytools__[k].update(new)
added.extend((k,v) for v in new)
res = None
for o in c:
if hasattr(o, '__allow__'):
Expand All @@ -101,18 +106,18 @@ def _wrap(v):
allow(k, allow_policy=v)
continue
vals = listify(v)
__pytools__[k].update(_wrap(x) for x in vals)
_add(k, *map(_wrap, vals))
for x in vals:
if isinstance(name := x[0] if isinstance(x, tuple) else x, str): _set_wrapped(k, name)
else:
res = track_call(o) if callable(o) else o
if not isinstance(o, type) and callable(o) and not inspect.isroutine(o) and not hasattr(o, '__qualname__'):
__pytools__[o].add(_wrap('__call__'))
_add(o, _wrap('__call__'))
_set_wrapped(type(o), '__call__')
continue
objclass = getattr(o, '__objclass__', None)
if objclass is not None:
__pytools__[objclass].add(_wrap(o.__name__))
_add(objclass, _wrap(o.__name__))
_set_wrapped(objclass, o.__name__, o)
continue
qualname = getattr(o, '__qualname__', '') or ''
Expand All @@ -122,11 +127,12 @@ def _wrap(v):
if '.' in qualname:
cls = getattr(mod, qualname.rsplit('.', 1)[0], None)
if cls is not None:
__pytools__[cls].add(_wrap(o.__name__))
_add(cls, _wrap(o.__name__))
_set_wrapped(cls, o.__name__, o)
continue
__pytools__[mod].add(_wrap(o.__name__))
_add(mod, _wrap(o.__name__))
_set_wrapped(mod, o.__name__, o)
if added: sys.audit('pyskills.allowed', added)
if len(c)==1 and callable(c[0]): return res

# %% ../nbs/00_core.ipynb #a3124a91
Expand Down