{"id":16347094,"url":"https://github.com/anexen/import-guard","last_synced_at":"2025-11-09T02:30:27.400Z","repository":{"id":144025987,"uuid":"377422905","full_name":"Anexen/import-guard","owner":"Anexen","description":"Enforce that some modules can't be imported from other modules. In runtime!","archived":false,"fork":false,"pushed_at":"2021-06-23T21:33:41.000Z","size":55,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-29T02:45:51.568Z","etag":null,"topics":["code-style","deny-imports","import-rules","python-imports"],"latest_commit_sha":null,"homepage":"","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/Anexen.png","metadata":{"files":{"readme":"README.md","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":"2021-06-16T08:20:41.000Z","updated_at":"2023-09-01T06:10:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"5ed49600-c9ed-4b21-9566-cd7f686bab8e","html_url":"https://github.com/Anexen/import-guard","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anexen%2Fimport-guard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anexen%2Fimport-guard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anexen%2Fimport-guard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anexen%2Fimport-guard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Anexen","download_url":"https://codeload.github.com/Anexen/import-guard/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239565664,"owners_count":19660158,"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":["code-style","deny-imports","import-rules","python-imports"],"created_at":"2024-10-11T00:39:38.424Z","updated_at":"2025-11-09T02:30:27.365Z","avatar_url":"https://github.com/Anexen.png","language":"Python","readme":"# import-guard\n\nEnforce that some modules can't be imported from other modules. In runtime!\n\n\u003e If you need a static analysis tools, take a look at [flake8-import-graph](https://pypi.org/project/flake8-import-graph/).\n\nFeatures:\n\n- works in runtime\n- checks dynamic imports\n- customizable rules\n\n\u003e This library has some performance overhead.\n\u003e In some cases it may lead to 1.5-2x slower import time (and startup time respectively).\n\u003e It's recommended to enable import_guard only during the development.\n\n# Installation\n\n```bash\npip install import-guard\n```\n\n# Usage\n\n```python\nfrom import_guard import guard, mod\n\n\nguard.set_deny_rules({\n    # deny `csv` import from `test_proj` and submodules\n    \"test_proj\": \"csv\",  # the same as mod(\"csv\")\n    # deny `selenium` and top_level `test_proj.tasks` imports from test_proj.api\n    # but allow `test_proj.tasks` import inside the function (lazy import)\n    # the same as mod(\"selenium\") | (mod(\"test_proj.tasks\") \u0026 Flags.TopLevel)\n    \"test_proj.api\": [\"selenium\", mod.top_level(\"test_proj.tasks\")],\n    # deny `test_proj.api` and `test_proj.business_logic` imports from `test_proj.core`\n    \"test_proj.core\": mod.matches(r\"test_proj\\.(api|business_logic)\"),\n    # deny all imports except `logging` and `yaml`\n    \"test_proj.logging\": ~mod.explicit([\"logging\", \"yaml\"]),\n})\n\n# raise ForbiddenImportError\nguard.enable(strict=True)\n```\n\n# Rules\n\n\u003e the code below is copy-pastable into the Python interpreter\n\u003e and assumes the following imports:\n\n```python\nfrom importlib import reload\nfrom import_guard import guard, mod\n# enable guard in advance\nguard.enable()\n```\n\n#### Exact match\n\n```python\nguard.set_deny_rules({\"\u003cstdin\u003e\": \"decimal\"})\n# shortcut for mod(\"decimal\")\n\nfrom decimal import Decimal  # shows warning\n\nfrom enum import Enum  # ok\n```\n\n#### Explicit match\n\nConsider the following code:\n\n```python\nguard.set_deny_rules({\"\u003cstdin\u003e\": \"re\"})\n\nimport csv  # shows warning!\n```\n\nWhat happened?\n\n`csv` imports some modules under the hood, e.g. `re` or `io`.\nWe implicitly initiated loading of the `re` module through the `csv` module (rule matches at depth = 1).\nThis is the default behavior. You can check only explicit imports using `mod.explicit(\"re\")` function.\n\n```python\nguard.set_deny_rules({\"\u003cstdin\u003e\": mod.explicit(\"re\")})\nreload(csv)  # allowed\nimport re  # shows warning\n```\n\n#### Match multiple modules\n\n```python\nguard.set_deny_rules({\"\u003cstdin\u003e\": [\"logging\", \"json\"]})\n# the same as mod.any([\"logging\", \"json'])\n# the same as mod(\"logging\") | mod(\"json\")\n\n\nimport json  # shows warning\nfrom logging import getLogger  # shows warning\n```\n\n#### Match by regular expression\n\n```python\nguard.set_deny_rules({\"\u003cstdin\u003e\": mod.matches(\"log.*\")})\n\n# shows multiple warnings\nfrom logging.config import dictConfig\n```\n\n#### Inversion\n\n```python\nguard.set_deny_rules({\"\u003cstdin\u003e\": ~mod.matches(\"log.*\")})\n\nimport io # shows warning\n```\n\n#### Match only module-level imports\n\nIt's common practice doing a local import instead of a global one to break a\ncycle import or to postpone importing until you run code that actually needs\nthe module you're importing.\n\n```python\n# deny module-level imports\nguard.set_deny_rules({\"\u003cstdin\u003e\": mod.top_level(\"array\")})\n\ndef some_function():\n    import array  # allowed (lazy import)\n\nsome_function()\nimport array  # shows warning\n```\n\n#### Match star import\n\n```python\nguard.set_deny_rules({\"\u003cstdin\u003e\": mod.star(\"csv\")})\n\nfrom csv import *  # shows warning\n```\n\n#### Complex rules\n\nRules are very flexible. You can combine them together in a different ways\nand build very complex conditions.\n\n```python\nmod.explicit(\n    ~mod.top_level([\"math\", \"json\"])\n    | mod.matches(\"log.*\")\n)\n```\n\nNice examples:\n\n- deny non-lazy imports in some module:\n\n```python\nguard.set_deny_rules({\n    \"test_proj.business_logic\": mod.top_level(mod.matches(\".*\")),\n})\n```\n\n- deny start imports in project:\n\n```python\nguard.set_deny_rules({\n    \"test_proj\": mod.star(mod.explicit(mod.matches(\".*\"))),\n})\n```\n\n#### Non-strict mode\n\n```python\n# not enabled for `prod`\nif env == \"staging\":\n    # warn on forbidden import\n    guard.enable(strict=False)\nelif env == \"local\":\n    # raise ForbiddenImportError\n    guard.enable(strict=True)\n```\n\n#### Rules hierarchy\n\nThe set of deny rule for a module also affects its submodules.\n\n```python\nguard.set_deny_rules({\n    \"test_proj\": \"json\",\n    \"test_proj.api\": [\"selenum\", \"pandas\"],\n    \"test_proj.core\": \"celery\"\n})\n```\n\n`test_proj.core` disallows `json` and `celery` imports.\n`test_proj.api.views` disallows `json`, `selenium`, `pandas` imports.\n\n#### Lazy module\n\nConsider the following project structure:\n\n```python\n# main.py\nimport api\n\n# api.py\ndef view():\n    import tasks\n\n# tasks.py\nimport pandas\n```\n\nHere `main.py` imports `api`, which imports `tasks` lazily, which imports `pandas` at module level.\n`import_guard` handles this case as lazy module import and will think that pandas being imported lazily.\nThus, in this case, the following rules do not raise a warning:\n\n```python\nguard.set_deny_rules({\"tasks\": mod.top_level(\"pandas\")})\n```\n\n#### Custom module matcher\n\n```python\ndef is_relative_import(import_info, caller_info):\n    return import_info.level \u003e 1\n\n# deny relative import\nguard.set_deny_rules({\"proj\": mod.hook(is_relative_import)})\n\nfrom .api import view  # shows warning\nfrom proj.api import view  # ok\n```\n\n# Testing\n\n### Rules\n\nTesting rules directly:\n\n```python\nrule = mod.top_level(mod.matches(\".*\"))\n# True; mod1 imported at the module level in mod2\nrule.test(\"mod1\", caller=\"mod2\")\n# False; mod1 doesn't match the top_level constraint\nrule.test(\"mod1\", caller=\"\u003cstdin\u003e\", top_level=False)\n```\n\nTesting deny rules through the guard:\n\n```python\nguard.is_import_allowed(\"csv\", caller=\"test_proj.api\")  # False\nguard.is_import_allowed(\"logging\", caller=\"test_proj.api\")  # True\nguard.is_import_allowed(\"selenium\", caller=\"test_proj.api\")  # False\nguard.is_import_allowed(\n    \"test_proj.tasks\", caller=\"test_proj.api\"\n)  # False\nguard.is_import_allowed(\n    \"test_proj.tasks\", caller=\"test_proj.api\", top_level=False\n)  # True\n```\n\n### Unit tests\n\nTesting with current Python interpreter:\n\n```bash\n$ python -m unittest discover tests -v\n```\n\nTesting with different Python versions and interpreters:\n\n```bash\n$ tox\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanexen%2Fimport-guard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanexen%2Fimport-guard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanexen%2Fimport-guard/lists"}