Skip to main content

image image image image Checked with pyright Ruff

modguard

A Python tool to enforce a modular, decoupled package architecture.

Docs

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

pip install modguard

Usage

Add a Boundary to the __init__.py of the module you're creating an interface for.

# 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.

# 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.

# 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.

> 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.

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.

@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:

# 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.

from core import main # contains public and private members
# 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:

# 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

License

GNU GPLv3

Release files for modguard 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for modguard 0.7.1
File Size Uploaded
modguard-0.7.1.tar.gz 34.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for modguard 0.7.1
File Interpreter ABI Platform
modguard-0.7.1-py3-none-any.whl Python 3 none any Details

Total release size: 68.4 kB

Release files / modguard-0.7.1.tar.gz

Download URL modguard-0.7.1.tar.gz
Size 34.6 kB
Tags Source
SHA-256 checksum
How to use checksums
bd36e2a46c4a457ff50e6cea7d63bc22be98ee4e351fde070af9fdf24abe3d94
BLAKE2b-256 checksum
How to use checksums
d0757e2f22323f00fb9cb07377666b8fe12765102ff2b5b310f9009b326467ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.9.19

Release files / modguard-0.7.1-py3-none-any.whl

Download URL modguard-0.7.1-py3-none-any.whl
Size 33.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
551479c603a6c4760d3b69b35d0bd8c5a848cb493a34b0b2274fd56f9f02743a
BLAKE2b-256 checksum
How to use checksums
7c149787ba33b4cd0818bc8d87c470580ba799f29df2b109f822d0af70b7eb4d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.9.19

Release history Release notifications | RSS feed

This release

0.7.1 This release

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page