Skip to content

__main__

Private Methods

Private methods, if any (those starting with _), are documented for completeness but do not offer any stability guarantees. They may change or be removed at any time without notice.

CLI for Polytropos.

BuildAll

Bases: Application

Build sdist and wheels for all compatible Odoo releases.

Source code in src/polytropos/__main__.py
@Polytropos.subcommand("build-all")
class BuildAll(cli.Application):
    """Build sdist and wheels for all compatible Odoo releases."""

    path: str = "."
    start_release: str = "17.0"
    end_release: str = "19.0"

    def main(
        self, path: str = ".", start_release: str = "17.0", end_release: str = "19.0"
    ) -> int:
        """Build sdist and wheels for all compatible Odoo releases.

        Args:
            path: Directory for the module. Defaults to current directory.
            start_release: Release to start building wheels for (default: 17.0)
            end_release: Release to stop building wheels for (default: 19.0)

        Returns:
            Exit code (0 for success, non-zero for failure)
        """
        self.path = path
        self.start_release = start_release
        self.end_release = end_release

        target = Path(self.path).resolve()

        if not target.is_dir():
            print(f"Error: {target} is not a directory")
            return 1

        if not (target / "pyproject.toml").exists():
            print(f"Error: pyproject.toml not found in {target}")
            return 1

        # Check if uv is available
        if not shutil.which("uv"):
            print("Error: 'uv' is not installed or not in PATH")
            return 1

        pyproject_data = get_pyproject_data(target)
        module_name = pyproject_data["project"]["name"]

        # Create dist directory
        dist_dir = target / "dist"
        dist_dir.mkdir(exist_ok=True)

        print(f"Building {module_name} for Odoo releases...")

        # Build sdist first
        print("Building sdist...")
        exit_code, stdout, stderr = local["uv"].run(
            ("build", "--sdist", "--out-dir", str(dist_dir), str(target))
        )
        if exit_code != 0:
            print(f"Error: sdist build failed: {stderr}")
            return 1

        # Get release range
        start_ver = parse_release(self.start_release)
        end_ver = parse_release(self.end_release)

        # Build wheel for each compatible release
        current = start_ver
        while current <= end_ver:
            release_str = f"{current.major}.{current.minor}"
            try:
                check_release_compatibility(release_str, pyproject_data)
                print(f"Building wheel for Odoo {release_str}...")
                exit_code, stdout, stderr = local["uv"].run(
                    (
                        "build",
                        "--wheel",
                        "--config-setting",
                        f"odoo_release={release_str}",
                        "--out-dir",
                        str(dist_dir),
                        str(target),
                    ),
                )
                if exit_code != 0:
                    print(f"Error: wheel build failed for Odoo {release_str}: {stderr}")
                    return 1
            except ValueError:
                print(f"Skipping Odoo {release_str} (incompatible with dependencies)")
            current = parse_release(f"{current.major + 1}.0")

        print(f"Build complete. Files in {dist_dir}:")
        for f in sorted(dist_dir.iterdir()):
            print(f"  {f.name}")
        return 0

main(path='.', start_release='17.0', end_release='19.0')

Build sdist and wheels for all compatible Odoo releases.

Parameters:

Name Type Description Default
path str

Directory for the module. Defaults to current directory.

'.'
start_release str

Release to start building wheels for (default: 17.0)

'17.0'
end_release str

Release to stop building wheels for (default: 19.0)

'19.0'

Returns:

Type Description
int

Exit code (0 for success, non-zero for failure)

Source code in src/polytropos/__main__.py
def main(
    self, path: str = ".", start_release: str = "17.0", end_release: str = "19.0"
) -> int:
    """Build sdist and wheels for all compatible Odoo releases.

    Args:
        path: Directory for the module. Defaults to current directory.
        start_release: Release to start building wheels for (default: 17.0)
        end_release: Release to stop building wheels for (default: 19.0)

    Returns:
        Exit code (0 for success, non-zero for failure)
    """
    self.path = path
    self.start_release = start_release
    self.end_release = end_release

    target = Path(self.path).resolve()

    if not target.is_dir():
        print(f"Error: {target} is not a directory")
        return 1

    if not (target / "pyproject.toml").exists():
        print(f"Error: pyproject.toml not found in {target}")
        return 1

    # Check if uv is available
    if not shutil.which("uv"):
        print("Error: 'uv' is not installed or not in PATH")
        return 1

    pyproject_data = get_pyproject_data(target)
    module_name = pyproject_data["project"]["name"]

    # Create dist directory
    dist_dir = target / "dist"
    dist_dir.mkdir(exist_ok=True)

    print(f"Building {module_name} for Odoo releases...")

    # Build sdist first
    print("Building sdist...")
    exit_code, stdout, stderr = local["uv"].run(
        ("build", "--sdist", "--out-dir", str(dist_dir), str(target))
    )
    if exit_code != 0:
        print(f"Error: sdist build failed: {stderr}")
        return 1

    # Get release range
    start_ver = parse_release(self.start_release)
    end_ver = parse_release(self.end_release)

    # Build wheel for each compatible release
    current = start_ver
    while current <= end_ver:
        release_str = f"{current.major}.{current.minor}"
        try:
            check_release_compatibility(release_str, pyproject_data)
            print(f"Building wheel for Odoo {release_str}...")
            exit_code, stdout, stderr = local["uv"].run(
                (
                    "build",
                    "--wheel",
                    "--config-setting",
                    f"odoo_release={release_str}",
                    "--out-dir",
                    str(dist_dir),
                    str(target),
                ),
            )
            if exit_code != 0:
                print(f"Error: wheel build failed for Odoo {release_str}: {stderr}")
                return 1
        except ValueError:
            print(f"Skipping Odoo {release_str} (incompatible with dependencies)")
        current = parse_release(f"{current.major + 1}.0")

    print(f"Build complete. Files in {dist_dir}:")
    for f in sorted(dist_dir.iterdir()):
        print(f"  {f.name}")
    return 0

Gc

Bases: Application

Garbage-collect the Polytropos cache.

Source code in src/polytropos/__main__.py
@Polytropos.subcommand("gc")
class Gc(cli.Application):
    """Garbage-collect the Polytropos cache."""

    full = cli.Flag(
        ["--full"],
        help="Delete the entire Polytropos cache directory",
    )

    def main(self) -> int:
        """Run garbage collection."""
        gc(full=self.full)
        return 0

main()

Run garbage collection.

Source code in src/polytropos/__main__.py
def main(self) -> int:
    """Run garbage collection."""
    gc(full=self.full)
    return 0

Init

Bases: Application

Initialize a new Polytropos module.

Source code in src/polytropos/__main__.py
@Polytropos.subcommand("init")
class Init(cli.Application):
    """Initialize a new Polytropos module."""

    def main(self, path: str = ".") -> int:
        """Create a new Polytropos module."""
        target = Path(path).resolve()

        if target.is_file():
            print(f"Error: {target} is a file, not a directory")
            return 1

        if not target.exists():
            target.mkdir(parents=True)

        if not target.is_dir():
            print(f"Error: {target} is not a directory")
            return 1

        module_name = target.name
        if not module_name:
            module_name = target.parent.name

        # Convert underscores to hyphens for package name
        package_name = module_name.replace("_", "-")

        pyproject_path = target / "pyproject.toml"

        if pyproject_path.exists():
            print(f"Error: {pyproject_path} already exists")
            return 1

        template = textwrap.dedent(f"""\
            [build-system]
            requires = ["polytropos[build]"]
            build-backend = "polytropos.build"

            [project]
            name = "odoo-addon-{package_name}"
            version = "1.0.0"
            description = "Odoo module: {module_name}"
            requires-python = ">=3.11"
            authors = [
                {{name = "Your Name"}}
            ]
            license = {{text = "LGPL-3"}}
            dependencies = [
                "odoo>=19.0",
            ]

            [project.urls]
            Homepage = "https://github.com/your-org/{module_name}"

            [tool.polytropos]
            default_odoo_release = "19.0"

            # [[tool.polytropos.manifest]]
            # depends = ["base"]
            # Add version-specific overrides:
            # [[tool.polytropos.manifest]]
            # releases = ">=18"
            # application = true
        """)

        pyproject_path.write_text(template)
        print(f"Created {pyproject_path} for module '{module_name}'")

        gitignore_path = target / ".gitignore"
        gitignore_template = textwrap.dedent("""\
            __manifest__.py
            PKG-INFO
            __pycache__/
            dist/
        """)

        gitignore_path.write_text(gitignore_template)
        print(f"Created {gitignore_path}")

        init_path = target / "__init__.py"
        init_path.write_text("")
        print(f"Created {init_path}")
        return 0

main(path='.')

Create a new Polytropos module.

Source code in src/polytropos/__main__.py
def main(self, path: str = ".") -> int:
    """Create a new Polytropos module."""
    target = Path(path).resolve()

    if target.is_file():
        print(f"Error: {target} is a file, not a directory")
        return 1

    if not target.exists():
        target.mkdir(parents=True)

    if not target.is_dir():
        print(f"Error: {target} is not a directory")
        return 1

    module_name = target.name
    if not module_name:
        module_name = target.parent.name

    # Convert underscores to hyphens for package name
    package_name = module_name.replace("_", "-")

    pyproject_path = target / "pyproject.toml"

    if pyproject_path.exists():
        print(f"Error: {pyproject_path} already exists")
        return 1

    template = textwrap.dedent(f"""\
        [build-system]
        requires = ["polytropos[build]"]
        build-backend = "polytropos.build"

        [project]
        name = "odoo-addon-{package_name}"
        version = "1.0.0"
        description = "Odoo module: {module_name}"
        requires-python = ">=3.11"
        authors = [
            {{name = "Your Name"}}
        ]
        license = {{text = "LGPL-3"}}
        dependencies = [
            "odoo>=19.0",
        ]

        [project.urls]
        Homepage = "https://github.com/your-org/{module_name}"

        [tool.polytropos]
        default_odoo_release = "19.0"

        # [[tool.polytropos.manifest]]
        # depends = ["base"]
        # Add version-specific overrides:
        # [[tool.polytropos.manifest]]
        # releases = ">=18"
        # application = true
    """)

    pyproject_path.write_text(template)
    print(f"Created {pyproject_path} for module '{module_name}'")

    gitignore_path = target / ".gitignore"
    gitignore_template = textwrap.dedent("""\
        __manifest__.py
        PKG-INFO
        __pycache__/
        dist/
    """)

    gitignore_path.write_text(gitignore_template)
    print(f"Created {gitignore_path}")

    init_path = target / "__init__.py"
    init_path.write_text("")
    print(f"Created {init_path}")
    return 0

Polytropos

Bases: Application

Polytropos - Build backend for Odoo modules.

Source code in src/polytropos/__main__.py
class Polytropos(cli.Application):
    """Polytropos - Build backend for Odoo modules."""

main()

Entry point for the CLI.

Source code in src/polytropos/__main__.py
def main() -> int:
    """Entry point for the CLI."""
    return Polytropos.run()[1]