Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python packages with no namespace or multiple namespaces #86

Merged
merged 7 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions backend_addon/cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"feature_headless": ["1", "0"],
"__feature_headless": "{{ cookiecutter.feature_headless }}",
"__feature_distribution": "0",
"__package_name": "{{ cookiecutter.python_package_name | package_name }}",
"__package_namespace": "{{ cookiecutter.python_package_name | package_namespace }}",
"__package_namespaces": "{{ cookiecutter.python_package_name | package_namespaces }}",
"__package_path": "{{ cookiecutter.python_package_name | package_path }}",
"__folder_name": "{{ cookiecutter.python_package_name }}",
"__python_package_name_upper": "{{ cookiecutter.python_package_name | pascal_case }}",
"__profile_language": "en",
Expand Down Expand Up @@ -41,8 +41,8 @@
"cookieplone.filters.latest_plone",
"cookieplone.filters.use_prerelease_versions",
"cookieplone.filters.pascal_case",
"cookieplone.filters.package_name",
"cookieplone.filters.package_namespace"
"cookieplone.filters.package_namespaces",
"cookieplone.filters.package_path"
],
"__cookieplone_repository_path": "",
"__cookieplone_template": ""
Expand Down
38 changes: 26 additions & 12 deletions backend_addon/hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Post generation hook."""

import os
from collections import OrderedDict
from copy import deepcopy
from pathlib import Path

from cookieplone.settings import QUIET_MODE_VAR
from cookieplone.utils import console, files, git, plone

context: OrderedDict = {{cookiecutter}}
Expand All @@ -18,12 +20,20 @@


def handle_feature_headless(context: OrderedDict, output_dir: Path):
package_namespace = context.get("__package_namespace")
package_name = context.get("__package_name")
output_dir = output_dir / "src" / package_namespace / package_name
output_dir = output_dir / "src" / "packagename"
files.remove_files(output_dir, FEATURES_TO_REMOVE["feature_headless"])


def handle_create_namespace_packages(context: OrderedDict, output_dir: Path):
plone.create_namespace_packages(
output_dir / "src/packagename", context["python_package_name"]
)


def handle_format(context: OrderedDict, output_dir: Path):
plone.format_python_codebase(output_dir)


def handle_git_initialization(context: OrderedDict, output_dir: Path):
"""Initialize a GIT repository for the project codebase."""
git.initialize_repository(output_dir)
Expand All @@ -32,9 +42,11 @@ def handle_git_initialization(context: OrderedDict, output_dir: Path):
def main():
"""Final fixes."""
output_dir = Path().cwd()
is_subtemplate = os.environ.get(QUIET_MODE_VAR) == "1"
remove_headless = not int(
context.get("feature_headless")
) # {{ cookiecutter.__feature_headless }}
create_namespace_packages = not is_subtemplate
initialize_git = bool(
int(context.get("__backend_addon_git_initialize"))
) # {{ cookiecutter.__backend_addon_git_initialize }}
Expand All @@ -48,6 +60,16 @@ def main():
"Remove files used in headless setup",
remove_headless,
],
[
handle_create_namespace_packages,
"Create namespace packages",
create_namespace_packages,
],
[
handle_format,
"Format code",
backend_format,
],
[
handle_git_initialization,
"Initialize Git repository",
Expand All @@ -61,18 +83,10 @@ def main():
console.print(f" -> {title}")
func(new_context, output_dir)

# Run format
if backend_format:
plone.format_python_codebase(output_dir)

msg = """
[bold blue]{{ cookiecutter.title }}[/bold blue]
Now, enter the repository run the code formatter with:
make format
start coding, and push to your organization.
Now, enter the repository, start coding, and push to your organization.
Sorry for the convenience,
The Plone Community.
Expand Down
20 changes: 14 additions & 6 deletions backend_addon/hooks/pre_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import sys

try:
from cookieplone import __version__ as cookieplone_version
from cookieplone import data
from cookieplone.utils import commands, console, sanity

HAS_COOKIEPLONE = True
except ModuleNotFoundError:
HAS_COOKIEPLONE = False
print("This template should be run with cookieplone")
sys.exit(1)
from packaging.version import Version


SUPPORTED_PYTHON_VERSIONS = [
Expand All @@ -23,6 +24,16 @@
def sanity_check() -> data.SanityCheckResults:
"""Run sanity checks on the system."""
checks = [
data.SanityCheck(
"Cookieplone",
lambda: (
""
if Version(cookieplone_version) > Version("0.8.0.dev0")
else "This template requires Cookieplone 0.8 or higher."
),
[],
"error",
),
data.SanityCheck(
"Python",
commands.check_python_version,
Expand All @@ -36,9 +47,6 @@ def sanity_check() -> data.SanityCheckResults:

def main():
"""Validate context."""
if not HAS_COOKIEPLONE:
print("This template should be run with cookieplone")
sys.exit(1)

msg = """
Creating a new Plone Addon
Expand Down
50 changes: 43 additions & 7 deletions backend_addon/tests/test_cutter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Test cookiecutter generation with all features enabled."""

from copy import deepcopy
from pathlib import Path

import pytest
Expand Down Expand Up @@ -40,10 +41,8 @@ def test_root_files_generated(cutter_result, file_path):
@pytest.mark.parametrize("file_path", PKG_SRC_FILES)
def test_pkg_src_files_generated(cutter_result, file_path: str):
"""Check if distribution files were generated."""
package_namespace = cutter_result.context["__package_namespace"]
package_name = cutter_result.context["__package_name"]
file_path = file_path.format(package_name=package_name)
src_path = cutter_result.project_path / "src" / package_namespace / package_name
package_path = cutter_result.context["__package_path"]
src_path = cutter_result.project_path / "src" / package_path
path = src_path / file_path
assert path.exists()
assert path.is_file()
Expand All @@ -52,9 +51,8 @@ def test_pkg_src_files_generated(cutter_result, file_path: str):
@pytest.mark.parametrize("file_path", PKG_SRC_FEATURE_HEADLESS)
def test_pkg_src_feature_files_generated(cutter_result, file_path: str):
"""Check if feature-specific files were generated."""
package_namespace = cutter_result.context["__package_namespace"]
package_name = cutter_result.context["__package_name"]
src_path = cutter_result.project_path / "src" / package_namespace / package_name
package_path = cutter_result.context["__package_path"]
src_path = cutter_result.project_path / "src" / package_path
path = src_path / file_path
assert path.exists()
assert path.is_file()
Expand Down Expand Up @@ -89,3 +87,41 @@ def test_git_initialization_not_set(cookies, context_no_git):
cutter_result = cookies.bake(extra_context=context_no_git)
path = cutter_result.project_path
assert git.check_path_is_repository(path) is False


@pytest.fixture(scope="session")
def cutter_result_no_namespace(context, cookies_session) -> dict:
"""Cookiecutter context without namespace package."""
new_context = deepcopy(context)
new_context["python_package_name"] = "addon"
return cookies_session.bake(extra_context=new_context)


@pytest.fixture(scope="session")
def cutter_result_two_namespaces(context, cookies_session) -> dict:
"""Cookiecutter context with 2 namespace packages."""
new_context = deepcopy(context)
new_context["python_package_name"] = "foo.bar.baz"
return cookies_session.bake(extra_context=new_context)


@pytest.mark.parametrize("file_path", PKG_SRC_FILES)
def test_pkg_src_files_generated_without_namespace(
cutter_result_no_namespace, file_path: str
):
"""Check package contents with no namespaces."""
src_path = cutter_result_no_namespace.project_path / "src/addon"
path = src_path / file_path
assert path.exists()
assert path.is_file()


@pytest.mark.parametrize("file_path", PKG_SRC_FILES)
def test_pkg_src_files_generated_with_two_namespaces(
cutter_result_two_namespaces, file_path: str
):
"""Check package contents with 2 namespaces."""
src_path = cutter_result_two_namespaces.project_path / "src/foo/bar/baz"
path = src_path / file_path
assert path.exists()
assert path.is_file()
11 changes: 4 additions & 7 deletions backend_addon/tests/test_cutter_no_headless.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@ def test_root_files_generated(cutter_result, file_path):
@pytest.mark.parametrize("file_path", PKG_SRC_FILES)
def test_pkg_src_files_generated(cutter_result, file_path: str):
"""Check if distribution files were generated."""
package_namespace = cutter_result.context["__package_namespace"]
package_name = cutter_result.context["__package_name"]
file_path = file_path.format(package_name=package_name)
src_path = cutter_result.project_path / "src" / package_namespace / package_name
package_path = cutter_result.context["__package_path"]
src_path = cutter_result.project_path / "src" / package_path
path = src_path / file_path
assert path.exists()
assert path.is_file()
Expand All @@ -56,8 +54,7 @@ def test_pkg_src_files_generated(cutter_result, file_path: str):
@pytest.mark.parametrize("file_path", PKG_SRC_FEATURE_HEADLESS)
def test_pkg_src_headless_files_not_generated(cutter_result, file_path: str):
"""Check feature-specific files were not generated."""
package_namespace = cutter_result.context["__package_namespace"]
package_name = cutter_result.context["__package_name"]
src_path = cutter_result.project_path / "src" / package_namespace / package_name
package_path = cutter_result.context["__package_path"]
src_path = cutter_result.project_path / "src" / package_path
path = src_path / file_path
assert path.exists() is False
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
graft src/{{ cookiecutter.__package_namespace }}
graft src/{{ cookiecutter.__package_path }}
graft docs
graft news
graft tests
Expand Down
2 changes: 1 addition & 1 deletion backend_addon/{{ cookiecutter.__folder_name }}/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
},
license="GPL version 2",
packages=find_packages("src", exclude=["ez_setup"]),
namespace_packages=["{{ cookiecutter.__package_namespace }}"],
namespace_packages=[{{ cookiecutter.__package_namespaces }}],
package_dir={"": "src"},
include_package_data=True,
zip_safe=False,
Expand Down

This file was deleted.

1 change: 0 additions & 1 deletion frontend_addon/tests/test_cutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ def test_root_files_generated(cutter_result, file_path):
def test_pkg_src_files_generated(cutter_result, file_path: str):
"""Check if package files were generated."""
package_name = cutter_result.context["frontend_addon_name"]
file_path = file_path.format(package_name=package_name)
src_path = cutter_result.project_path / "packages" / package_name
path = src_path / file_path
assert path.exists()
Expand Down
24 changes: 5 additions & 19 deletions project/cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,10 @@
"frontend_addon_name": "volto-{{ cookiecutter.python_package_name|replace('_', '-')|replace('.', '-') }}",
"language_code": ["en", "de", "es", "pt-br", "nl", "fi"],
"github_organization": "collective",
"container_registry": [
"github",
"docker_hub",
"gitlab"
],
"devops_cache": [
"1",
"0"
],
"devops_ansible": [
"1",
"0"
],
"devops_gha_deploy": [
"1",
"0"
],
"container_registry": ["github", "docker_hub", "gitlab"],
"devops_cache": ["1", "0"],
"devops_ansible": ["1", "0"],
"devops_gha_deploy": ["1", "0"],
"__feature_headless": "1",
"__npm_package_name": "{{ cookiecutter.frontend_addon_name }}",
"__folder_name": "{{ cookiecutter.project_slug }}",
Expand Down Expand Up @@ -67,8 +54,8 @@
"__devops_varnish_version": "7.6",
"__devops_db_version": "14",
"__devops_db_password": "{{ random_ascii_string(12) }}",
"__backend_addon_format": "1",
"__backend_addon_git_initialize": "0",
"__backend_addon_format": "1",
"__project_git_initialize": "1",
"__prompts__": {
"title": "Project Title",
Expand Down Expand Up @@ -120,7 +107,6 @@
"devops/requirements",
"devops/tasks",
"devops/inventory/group_vars/all/users.yml"

],
"_extensions": [
"cookieplone.filters.use_prerelease_versions",
Expand Down
5 changes: 5 additions & 0 deletions project/hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ def main():
console.print(f" -> {title}")
func(new_context, output_dir)

# Create namespace packages
plone.create_namespace_packages(
output_dir / "backend/src/packagename", context["python_package_name"]
)

# Run format
if backend_format:
backend_folder = output_dir / "backend"
Expand Down
25 changes: 18 additions & 7 deletions project/hooks/pre_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import sys

from cookieplone import data
from cookieplone.utils import commands, console, sanity

HAS_COOKIEPLONE = True
try:
from cookieplone import __version__ as cookieplone_version
from cookieplone import data
from cookieplone.utils import commands, console, sanity
except ModuleNotFoundError:
print("This template should be run with cookieplone")
sys.exit(1)
from packaging.version import Version

SUPPORTED_PYTHON_VERSIONS = [
"3.8",
Expand All @@ -19,6 +23,16 @@
def sanity_check() -> data.SanityCheckResults:
"""Run sanity checks on the system."""
checks = [
data.SanityCheck(
"Cookieplone",
lambda: (
""
if Version(cookieplone_version) > Version("0.8.0.dev0")
else "This template requires Cookieplone 0.8 or higher."
),
[],
"error",
),
data.SanityCheck(
"Python",
commands.check_python_version,
Expand All @@ -41,9 +55,6 @@ def sanity_check() -> data.SanityCheckResults:

def main():
"""Validate context."""
if not HAS_COOKIEPLONE:
print("This template should be run with cookieplone")
sys.exit(1)

msg = """
Creating a new Plone Project
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ isort
pytest
pytest-cookies
pytest-jsonschema >= 1.0.0a2
cookieplone>=0.7.0
cookieplone >= 0.8.0
GitPython
wheel
Loading