Show HN: Localscope–Limit scope of Python functions for reproducible execution
localscope.readthedocs.io
localscope.readthedocs.io
[1]: https://docs.hhvm.com/hack/contexts-and-capabilities/introdu...
for instruction in dis.get_instructions(code):
...
Code here: https://github.com/tillahoffmann/localscope/blob/092392d5bdb... $ txr -i localscope.tl
Pour le service en la langue Shell, appuyez sur Ctrl-D.
1> (localscope (+ %pi% %pi%)) ;; OK, %pi% is built-in
6.28318530717959
2> (localscope *print-base*) ;; likewise
10
3> (localscope (+ a b c)) ;; bad
** expr-3:1: localscope: global variables (c b a) used
Code: (defmacro localscope (:form f :env e . forms)
(let ((body ^(progn ,*forms)))
(tree-bind (exp fv-inner ff-inner fv-outer ff-outer) (expand-with-free-refs
body e)
(ignore ff-inner fv-outer ff-outer)
(let* ((usr (find-package :usr))
(globals [keep-if (do or (not (boundp @1))
(neq (symbol-package @1) usr))
fv-inner]))
(if globals
(compile-error f "global variables ~s used" globals)))
body)))
We get a list of the free variable references emanating from the enclosed forms, and filter them: we are interested in all that do not have bindings as globals in the standard library. I.e. either are not bound at all, or else if they are, are not symbols in the usr: ("user space") package. 1> (let (x) (localscope (+ x y)))
** expr-1:1: localscope: global variables (y) usedThis is actually a cleaner API so might switch my code to it, amazing work
You see a problem, you fix it with library, and I applaud that. You have to wonder though, how many years does it take for a reproducible notebook environment to implement out of scope variable guards..
Luckily there are new approaches, e.g. Marimo and Pluto, that don't have the same root issue.
It's just a safeguard for well-intentioned people to prevent themselves from making mistakes with their existing tools, instead of changing to a completely different set of tools.
Would be interesting to see how it compares to static analysis tools like mypy or linters—does it catch edge cases they might miss? Nice work!
Nope!
> Have you ever scratched your head because your code broke after restarting the Python kernel?
Also nope!
I'm technically a data scientist but I don't use notebooks very often. Even when I did, though, I can't recall having this error very often. Is this really necessary? Anyway, this kind of thing is only useful if people adopt it. The people who are likely to install this tool and use it probably already avoid this issue while the people who have this issue will never hear of this tool or will not consistently employ it.
I guess I'm just salty from years of dealing with people who give their Python code half-hearted incorrect type hints but never run a static type checker on their code.
I could see projects adopting this, or something like it, in their CI pipelines.
Both for open source projects, but also at companies with a lot of internal repositories with Python code – especially if it is common that other people with little experience for some repo contribute code to it.
In the case of an open source project if it’s a popular repo that a lot of people are users of and different people frequently contribute to for the first time.
And in the case of company internal repositories, if it’s code that different teams only touch now and then, with only a smaller group of people intimately familiar with all of the inner workings of the code and the other people being mainly focused on other repos in the company but still having to sometimes contribute changes because their code is relying on the code in that other repos.
The decorator syntax is purely sugar:
@foo
def bar():
pass
is equivalent to def bar():
pass
bar = foo(bar)
except that `bar` only gets assigned once.It's possible to use metaprogramming to apply the decorator automatically to every function (by iterating over attributes of the module and filtering for functions).
I've hit it in both R and Python in interactive sessions. Otherwise it's generally a non issue.
I can definitely see this being helpful, particularly for the intended context. It would also be useful when you need to move notebook code to a different environment.
I completely see the value in this tool for a particular style of programming.