modguard


Namemodguard JSON
Version 0.7.1 PyPI version JSON
download
home_pageNone
SummaryA Python tool to maintain clean dependencies across python modules.
upload_time2024-04-21 05:54:07
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords python module guard enforcement enforcer decorator subclass domain architecture
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            [![image](https://img.shields.io/pypi/v/modguard.svg)](https://pypi.python.org/pypi/modguard)
[![image](https://img.shields.io/pypi/l/modguard.svg)](https://pypi.python.org/pypi/modguard)
[![image](https://img.shields.io/pypi/pyversions/modguard.svg)](https://pypi.python.org/pypi/modguard)
[![image](https://github.com/Never-Over/modguard/actions/workflows/ci.yml/badge.svg)](https://github.com/Never-Over/modguard/actions/workflows/ci.yml)
[![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
# modguard
A Python tool to enforce a modular, decoupled package architecture.

![](https://raw.githubusercontent.com/Never-Over/modguard/main/docs/modguard_screencap_2x.gif)

[Docs](https://never-over.github.io/modguard/)

### What is modguard?
Modguard enables you to define the public interface for a given module. Marking a package with a `Boundary` makes all of its internals private by default, exposing only the members marked with  `public`.

This enforces an architecture of decoupled modules, and ensures the communication between domains only happens through their defined public interfaces.

Modguard is incredibly lightweight, and has no impact on the runtime of your code. Instead, its checks are performed through a static analysis CLI tool.

### Installation
```bash
pip install modguard
```
### Usage
Add a `Boundary` to the `__init__.py` of the module you're creating an interface for.
```python
# project/core/__init__.py
import modguard

modguard.Boundary()
```
Add the `public` decorator to any callable in the module that should be exported. You can also export individual members by passing them to `public` as function call arguments.
```python
# project/core/main.py
import modguard

# Adding the decorator here signifies this function is public
@modguard.public
def public_function(user_id: int) -> str:
    ...

# This function will be considered private
def private_function():
    ...

PUBLIC_CONSTANT = "Hello world"
# Allow export of PUBLIC_CONSTANT from this module
public(PUBLIC_CONSTANT)
```
Modguard will now flag any incorrect dependencies between modules.
```bash
# From the root of your python project (in this example, `project/`)
> modguard check .
❌ ./utils/helpers.py: Import "core.main.private_function" in ./utils/helpers.py is blocked by boundary "core.main"
```
You can also view your entire project's set of dependencies and public interfaces. Boundaries will be marked with a `[B]`, and public members will be marked with a `[P]`. Note that a module can be both public and a boundary.
```bash
> modguard show .
example
  [B]core
    main
      [P]public_function
      [P]PUBLIC_CONSTANT
  [P][B]utils
    helpers
```
If you want to utilize this data for other purposes, run `modguard show --write .` This will persist the data about your project in a `modguard.yaml` file.
### Setup
Modguard also comes bundled with a command to set up and define your initial boundaries.
```bash
modguard init .
```
By running `modguard init` from the root of your python project, modguard will inspect and declare boundaries on each python package within your project. Additionally, each accessed member of that package will be decorated with `public`.

This will automatically create boundaries and define your public interface for each package within your project, and instantly get your project to a passing state for `modguard .`


### Advanced
Modguard also supports specific allow lists within `public`. The `allowlist` parameter accepts a list of strings and regex expressions.
```python
@modguard.public(allowlist=["utils.helpers", r"core\.project\.*"])
def public_function(user_id: int) -> str:
    ...

PUBLIC_CONSTANT = "Hello world"
public(PUBLIC_CONSTANT, allowlist=["utils.helpers", r"core\.project\.*"])

```
This will allow for `public_function` and `PUBLIC_CONSTANT` to be imported and used in `utils.helpers` and any matching regex to `core\.project\.*`, but restrict its usage elsewhere.

Alternatively, you can mark an import with the `modguard-ignore` comment:
```python
# modguard-ignore
from core.main import private_function
```
This will stop modguard from flagging this import as a boundary violation.

Given that python allows for dynamic importing at runtime, modguard will fail if a whole module is imported without being declared public.
```python
from core import main # contains public and private members
```
```bash
# From the root of your project
> modguard .
❌ ./utils/helpers.py: Import "core.main" in ./utils/helpers.py is blocked by boundary "core.main"
```

If you expect to be able to import the entire contents of your module, you can declare an entire module as public to avoid this:
```python
# core/main.py
import modguard

modguard.public()

...
```
This syntax also supports the `allowlist` parameter.

### Details
Modguard works by analyzing the abstract syntax tree (AST) of your codebase. The `Boundary` class and `@public` decorator have no runtime impact, and are detected by modguard statically. 

Boundary violations are detected at the import layer. This means that specific nonstandard custom syntax to access modules/submodules such as getattr or dynamically generated namespaces will not be caught by modguard.

[PyPi Package](https://pypi.org/project/modguard/)

### License
[GNU GPLv3](LICENSE)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "modguard",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "python, module, guard, enforcement, enforcer, decorator, subclass, domain, architecture",
    "author": null,
    "author_email": "Caelean Barnes <caeleanb@gmail.com>, Evan Doyle <evanmdoyle@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/d0/75/7e2f22323f00fb9cb07377666b8fe12765102ff2b5b310f9009b326467ba/modguard-0.7.1.tar.gz",
    "platform": null,
    "description": "[![image](https://img.shields.io/pypi/v/modguard.svg)](https://pypi.python.org/pypi/modguard)\n[![image](https://img.shields.io/pypi/l/modguard.svg)](https://pypi.python.org/pypi/modguard)\n[![image](https://img.shields.io/pypi/pyversions/modguard.svg)](https://pypi.python.org/pypi/modguard)\n[![image](https://github.com/Never-Over/modguard/actions/workflows/ci.yml/badge.svg)](https://github.com/Never-Over/modguard/actions/workflows/ci.yml)\n[![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n# modguard\nA Python tool to enforce a modular, decoupled package architecture.\n\n![](https://raw.githubusercontent.com/Never-Over/modguard/main/docs/modguard_screencap_2x.gif)\n\n[Docs](https://never-over.github.io/modguard/)\n\n### What is modguard?\nModguard enables you to define the public interface for a given module. Marking a package with a `Boundary` makes all of its internals private by default, exposing only the members marked with  `public`.\n\nThis enforces an architecture of decoupled modules, and ensures the communication between domains only happens through their defined public interfaces.\n\nModguard is incredibly lightweight, and has no impact on the runtime of your code. Instead, its checks are performed through a static analysis CLI tool.\n\n### Installation\n```bash\npip install modguard\n```\n### Usage\nAdd a `Boundary` to the `__init__.py` of the module you're creating an interface for.\n```python\n# project/core/__init__.py\nimport modguard\n\nmodguard.Boundary()\n```\nAdd the `public` decorator to any callable in the module that should be exported. You can also export individual members by passing them to `public` as function call arguments.\n```python\n# project/core/main.py\nimport modguard\n\n# Adding the decorator here signifies this function is public\n@modguard.public\ndef public_function(user_id: int) -> str:\n    ...\n\n# This function will be considered private\ndef private_function():\n    ...\n\nPUBLIC_CONSTANT = \"Hello world\"\n# Allow export of PUBLIC_CONSTANT from this module\npublic(PUBLIC_CONSTANT)\n```\nModguard will now flag any incorrect dependencies between modules.\n```bash\n# From the root of your python project (in this example, `project/`)\n> modguard check .\n\u274c ./utils/helpers.py: Import \"core.main.private_function\" in ./utils/helpers.py is blocked by boundary \"core.main\"\n```\nYou can also view your entire project's set of dependencies and public interfaces. Boundaries will be marked with a `[B]`, and public members will be marked with a `[P]`. Note that a module can be both public and a boundary.\n```bash\n> modguard show .\nexample\n  [B]core\n    main\n      [P]public_function\n      [P]PUBLIC_CONSTANT\n  [P][B]utils\n    helpers\n```\nIf you want to utilize this data for other purposes, run `modguard show --write .` This will persist the data about your project in a `modguard.yaml` file.\n### Setup\nModguard also comes bundled with a command to set up and define your initial boundaries.\n```bash\nmodguard init .\n```\nBy running `modguard init` from the root of your python project, modguard will inspect and declare boundaries on each python package within your project. Additionally, each accessed member of that package will be decorated with `public`.\n\nThis will automatically create boundaries and define your public interface for each package within your project, and instantly get your project to a passing state for `modguard .`\n\n\n### Advanced\nModguard also supports specific allow lists within `public`. The `allowlist` parameter accepts a list of strings and regex expressions.\n```python\n@modguard.public(allowlist=[\"utils.helpers\", r\"core\\.project\\.*\"])\ndef public_function(user_id: int) -> str:\n    ...\n\nPUBLIC_CONSTANT = \"Hello world\"\npublic(PUBLIC_CONSTANT, allowlist=[\"utils.helpers\", r\"core\\.project\\.*\"])\n\n```\nThis will allow for `public_function` and `PUBLIC_CONSTANT` to be imported and used in `utils.helpers` and any matching regex to `core\\.project\\.*`, but restrict its usage elsewhere.\n\nAlternatively, you can mark an import with the `modguard-ignore` comment:\n```python\n# modguard-ignore\nfrom core.main import private_function\n```\nThis will stop modguard from flagging this import as a boundary violation.\n\nGiven that python allows for dynamic importing at runtime, modguard will fail if a whole module is imported without being declared public.\n```python\nfrom core import main # contains public and private members\n```\n```bash\n# From the root of your project\n> modguard .\n\u274c ./utils/helpers.py: Import \"core.main\" in ./utils/helpers.py is blocked by boundary \"core.main\"\n```\n\nIf you expect to be able to import the entire contents of your module, you can declare an entire module as public to avoid this:\n```python\n# core/main.py\nimport modguard\n\nmodguard.public()\n\n...\n```\nThis syntax also supports the `allowlist` parameter.\n\n### Details\nModguard works by analyzing the abstract syntax tree (AST) of your codebase. The `Boundary` class and `@public` decorator have no runtime impact, and are detected by modguard statically. \n\nBoundary violations are detected at the import layer. This means that specific nonstandard custom syntax to access modules/submodules such as getattr or dynamically generated namespaces will not be caught by modguard.\n\n[PyPi Package](https://pypi.org/project/modguard/)\n\n### License\n[GNU GPLv3](LICENSE)\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A Python tool to maintain clean dependencies across python modules.",
    "version": "0.7.1",
    "project_urls": {
        "Homepage": "https://github.com/never-over/modguard",
        "Issues": "https://github.com/never-over/modguard/issues"
    },
    "split_keywords": [
        "python",
        " module",
        " guard",
        " enforcement",
        " enforcer",
        " decorator",
        " subclass",
        " domain",
        " architecture"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7c149787ba33b4cd0818bc8d87c470580ba799f29df2b109f822d0af70b7eb4d",
                "md5": "9e652336de5fd7642f9f2e53579e9df7",
                "sha256": "551479c603a6c4760d3b69b35d0bd8c5a848cb493a34b0b2274fd56f9f02743a"
            },
            "downloads": -1,
            "filename": "modguard-0.7.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9e652336de5fd7642f9f2e53579e9df7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 33808,
            "upload_time": "2024-04-21T05:54:06",
            "upload_time_iso_8601": "2024-04-21T05:54:06.319161Z",
            "url": "https://files.pythonhosted.org/packages/7c/14/9787ba33b4cd0818bc8d87c470580ba799f29df2b109f822d0af70b7eb4d/modguard-0.7.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d0757e2f22323f00fb9cb07377666b8fe12765102ff2b5b310f9009b326467ba",
                "md5": "c7ef6277627e75fa2b6d40d444371d42",
                "sha256": "bd36e2a46c4a457ff50e6cea7d63bc22be98ee4e351fde070af9fdf24abe3d94"
            },
            "downloads": -1,
            "filename": "modguard-0.7.1.tar.gz",
            "has_sig": false,
            "md5_digest": "c7ef6277627e75fa2b6d40d444371d42",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 34620,
            "upload_time": "2024-04-21T05:54:07",
            "upload_time_iso_8601": "2024-04-21T05:54:07.986515Z",
            "url": "https://files.pythonhosted.org/packages/d0/75/7e2f22323f00fb9cb07377666b8fe12765102ff2b5b310f9009b326467ba/modguard-0.7.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-21 05:54:07",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "never-over",
    "github_project": "modguard",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "modguard"
}
        
Elapsed time: 3.25958s