{"id":16536301,"url":"https://github.com/tillahoffmann/localscope","last_synced_at":"2025-03-21T09:32:14.989Z","repository":{"id":62576571,"uuid":"304143430","full_name":"tillahoffmann/localscope","owner":"tillahoffmann","description":"Restrict the scope of functions for reproducible code execution and peace of mind.","archived":false,"fork":false,"pushed_at":"2025-03-17T17:38:16.000Z","size":54,"stargazers_count":10,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-17T18:33:54.615Z","etag":null,"topics":["jupyter","machine-learning","python","reproducibility","research"],"latest_commit_sha":null,"homepage":"https://localscope.readthedocs.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tillahoffmann.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-14T21:49:33.000Z","updated_at":"2025-03-17T18:29:05.000Z","dependencies_parsed_at":"2024-05-15T12:41:41.466Z","dependency_job_id":"939c4aa1-35df-471f-ad2c-e1d0352d4f3d","html_url":"https://github.com/tillahoffmann/localscope","commit_stats":{"total_commits":6,"total_committers":1,"mean_commits":6.0,"dds":0.0,"last_synced_commit":"3f757edadfa042cbbb90e98cddc2c242be74428b"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillahoffmann%2Flocalscope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillahoffmann%2Flocalscope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillahoffmann%2Flocalscope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillahoffmann%2Flocalscope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tillahoffmann","download_url":"https://codeload.github.com/tillahoffmann/localscope/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244772553,"owners_count":20508009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jupyter","machine-learning","python","reproducibility","research"],"created_at":"2024-10-11T18:30:25.168Z","updated_at":"2025-03-21T09:32:14.984Z","avatar_url":"https://github.com/tillahoffmann.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"🔐 localscope\n=============\n\n.. image:: https://img.shields.io/static/v1?label=\u0026message=GitHub\u0026color=gray\u0026logo=github\n    :target: https://github.com/tillahoffmann/localscope\n\n.. image:: https://github.com/tillahoffmann/localscope/actions/workflows/build.yml/badge.svg\n  :target: https://github.com/tillahoffmann/localscope/actions/workflows/build.yml\n\n.. image:: https://readthedocs.org/projects/localscope/badge/?version=latest\n  :target: https://localscope.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://img.shields.io/pypi/v/localscope.svg\n   :target: https://pypi.python.org/pypi/localscope\n\n.. image:: https://static.pepy.tech/badge/localscope\n   :target: https://pepy.tech/projects/localscope\n\nHave you ever hunted bugs caused by accidentally using a global variable in a function in a `Jupyter notebook \u003chttps://jupyter.org/\u003e`__? Have you ever scratched your head because your code broke after restarting the Python kernel? localscope can help by restricting the variables a function can access.\n\n.. doctest::\n\n   \u003e\u003e\u003e from localscope import localscope\n   \u003e\u003e\u003e\n   \u003e\u003e\u003e a = 'hello world'\n   \u003e\u003e\u003e\n   \u003e\u003e\u003e @localscope\n   ... def print_a():\n   ...     print(a)\n   Traceback (most recent call last):\n     ...\n   localscope.LocalscopeException: `a` is not a permitted global (file \"...\",\n      line 1, in print_a)\n\nSee the :ref:`interface` section for an exhaustive list of options and the :ref:`motivation` for a more detailed example.\n\nInstallation\n------------\n\n.. code-block:: bash\n\n   $ pip install localscope\n\n.. _interface:\n\nInterface\n---------\n\n.. autofunction:: localscope.localscope\n\n.. _motivation:\n\nMotivation and Example\n----------------------\n\nInteractive python sessions are outstanding tools for analysing data, generating visualisations, and training machine learning models. However, the interactive nature allows global variables to leak into the scope of functions accidentally, leading to unexpected behaviour. For example, suppose you are evaluating the mean squared error between two lists of numbers, including a scale factor ``sigma``.\n\n.. doctest::\n\n   \u003e\u003e\u003e sigma = 7\n   \u003e\u003e\u003e # [other notebook cells and bits of code]\n   \u003e\u003e\u003e xs = [1, 2, 3]\n   \u003e\u003e\u003e ys = [4, 5, 6]\n   \u003e\u003e\u003e mse = sum(((x - y) / sigma) ** 2 for x, y in zip(xs, ys))\n   \u003e\u003e\u003e mse\n   0.55102...\n\nEverything works nicely, and you package the code in a function for later use but forget about the scale factor introduced earlier in the notebook.\n\n.. doctest::\n\n   \u003e\u003e\u003e def evaluate_mse(xs, ys):  # missing argument sigma\n   ...     return sum(((x - y) / sigma) ** 2 for x, y in zip(xs, ys))\n   \u003e\u003e\u003e\n   \u003e\u003e\u003e mse = evaluate_mse(xs, ys)\n   \u003e\u003e\u003e mse\n   0.55102...\n\nThe variable ``sigma`` is obtained from the global scope, and the code executes without any issue. But the output is affected by changing the value of sigma.\n\n.. doctest::\n\n   \u003e\u003e\u003e sigma = 13\n   \u003e\u003e\u003e evaluate_mse(xs, ys)\n   0.15976...\n\nThis example may seem contrived. But unintended information leakage from the global scope to the local function scope often leads to unreproducible results, hours spent debugging, and many kernel restarts to identify the source of the problem. Localscope fixes this problem by restricting the allowed scope.\n\n.. doctest::\n\n   \u003e\u003e\u003e @localscope\n   ... def evaluate_mse(xs, ys):  # missing argument sigma\n   ...     return sum(((x - y) / sigma) ** 2 for x, y in zip(xs, ys))\n   Traceback (most recent call last):\n     ...\n   localscope.LocalscopeException: `sigma` is not a permitted global (file \"...\",\n      line 3, in \u003cgenexpr\u003e)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftillahoffmann%2Flocalscope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftillahoffmann%2Flocalscope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftillahoffmann%2Flocalscope/lists"}