Repository: stonebase-mx/python-packages.stonebase.mx (language: python)

Issue:
Create cookiecutter template for package extensions



Discussion / hints:

Fix patch:
diff --git a/cookiecutter.sh b/cookiecutter.sh
new file mode 100644
index 0000000..131ecb3
--- /dev/null
+++ b/cookiecutter.sh
@@ -0,0 +1,1 @@
+cookiecutter extensions --output-dir extensions
diff --git a/extensions/cookiecutter.json b/extensions/cookiecutter.json
new file mode 100644
index 0000000..0af39a2
--- /dev/null
+++ b/extensions/cookiecutter.json
@@ -0,0 +1,5 @@
+{
+    "project_name": "RHDZMOTA-EXTENSION-APP",
+    "project_slug": "{{cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_')}}",
+    "project_version": "0.1.0"
+}
diff --git a/extensions/{{cookiecutter.project_slug}}/CHANGELOG.md b/extensions/{{cookiecutter.project_slug}}/CHANGELOG.md
new file mode 100644
index 0000000..c00a8b0
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Extension Changelog: {{cookiecutter.project_slug}}
+
+Release types:
+* `major`: Non-backward compatible new features or refactors, or bugfixes.
+* `minor`: Backward compatible new features, refactors, or bugfixes.
+* `patch`: Minor backward compatible changes such as typos, documentations, small bugs, etc.
+
+## `0.1.0` Genesis - First Implementation
+
+* Date: `TBD`
+* Release Type: `minor`
diff --git a/extensions/{{cookiecutter.project_slug}}/LICENSE b/extensions/{{cookiecutter.project_slug}}/LICENSE
new file mode 100644
index 0000000..e69de29
diff --git a/extensions/{{cookiecutter.project_slug}}/MANIFEST.in b/extensions/{{cookiecutter.project_slug}}/MANIFEST.in
new file mode 100644
index 0000000..c9fdcd8
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/MANIFEST.in
@@ -0,0 +1,4 @@
+include LICENSE
+include README.md
+include requirements.txt
+include src/main/rhdzmota/ext/{{cookiecutter.project_slug}}_version
diff --git a/extensions/{{cookiecutter.project_slug}}/README.md b/extensions/{{cookiecutter.project_slug}}/README.md
new file mode 100644
index 0000000..455fb1f
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/README.md
@@ -0,0 +1,35 @@
+# RHDZMOTA EXT: {{cookiecutter.project_slug}}
+
+Package extensions have 2 main purposes:
+* Split the dependency sets into several smaller python projects
+* Serve as domain-specific packages (i.e., split functionality).
+
+## About the `{{cookiecutter.project_slug}}` extension
+
+..add description..
+
+## Linked Installation
+
+Most extensions can be installed indirectly via the main package by providing an extra-dependency tag. Example:
+
+```commandline
+$ pip install 'rhdzmota[ext.hello_world]'
+```
+* General pattern: `'rhdzmota[ext.<extension-slug>]'`
+
+## Standalone Installations
+
+Package extensions can also be installed independently. For example:
+
+```commandline
+$ pip install rhdzmota_extension_hello_world
+```
+* General pattern: `rhdzmota_extension_<extension-slug>`
+
+You can also install them locally:
+
+```commandline
+$ EXT_BUILD_LOCAL=1 pip install -e extensions/hello_world
+```
+* General pattern: `EXT_BUILD_LOCAL=1 pip install -e path/to/extension`
+
diff --git a/extensions/{{cookiecutter.project_slug}}/requirements.txt b/extensions/{{cookiecutter.project_slug}}/requirements.txt
new file mode 100644
index 0000000..46ab131
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/requirements.txt
@@ -0,0 +1,2 @@
+# Should always have `rhdzmota` as a dependency
+rhdzmota
diff --git a/extensions/{{cookiecutter.project_slug}}/setup.py b/extensions/{{cookiecutter.project_slug}}/setup.py
new file mode 100644
index 0000000..c5ba1ef
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/setup.py
@@ -0,0 +1,77 @@
+import os
+from setuptools import setup, find_namespace_packages
+
+# Environ variables
+EXT_CODEBASE_PATH = os.environ.get(
+    "EXT_CODEBASE_PATH",
+    default=os.path.join("src", "main")
+)
+EXT_BUILD_LOCAL = int(os.environ.get(
+    "EXT_BUILD_LOCAL",
+    default="0",
+))
+
+with open("README.md", "r") as file:
+    readme = file.read()
+
+with open("requirements.txt", "r") as file:
+    requirements = [
+        req.strip()
+        for req in file.read().splitlines()
+        if req and not req.startswith("#")
+    ]
+    # If local build, do not install `rhdzmota` from pypi
+    requirements.pop(requirements.index("rhdzmota"))
+
+version_filename = "{{cookiecutter.project_slug}}_version"
+version_filepath = os.path.join(
+    EXT_CODEBASE_PATH,
+    "rhdzmota",
+    "ext",
+    version_filename,
+)
+
+with open(version_filepath, "r") as file:
+    version = file.readline().strip()
+
+setup(
+    name="rhdzmota_extension_{{cookiecutter.project_slug}}",
+    version=version,
+    description=(
+        "RHDZMOTA Extension App: {{cookiecutter.project_slug}}"
+    ),
+    long_description=readme,
+    long_description_content_type="text/markdown",
+    author="Rodrigo H. Mota",
+    author_email="info@rhdzmota.com",
+    url="https://github.com/rhdzmota/package.rhdzmota.com",
+    classifiers=[
+        "Typing :: Typed",
+        "Topic :: Scientific/Engineering",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        "Programming Language :: Python :: 3 :: Only",
+        "Programming Language :: Python :: 3.10",
+    ],
+    package_dir={
+        "": EXT_CODEBASE_PATH,
+    },
+    package_data={
+        "": [
+            os.path.join(
+                "rhdzmota",
+                "ext",
+                "{{cookiecutter.project_slug}}_version",
+            ),
+        ],
+    },
+    packages=[
+        package
+        for package in find_namespace_packages(where=EXT_CODEBASE_PATH)
+        if package.startswith("rhdzmota")
+    ],
+    include_package_data=True,
+    install_requires=requirements,
+    python_requires=">=3.10, <4",
+    license="TBD",
+    zip_safe=False,
+)
diff --git a/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}/__init__.py b/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}/settings.py b/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}/settings.py
new file mode 100644
index 0000000..8269c3b
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}/settings.py
@@ -0,0 +1,9 @@
+import os
+import enum
+
+
+class Env(enum.Enum):
+    EXT_APP_NAME = os.environ.get(
+        "EXT_APP_NAME",
+        default="{{cookiecutter.project_slug}}",
+    )
diff --git a/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}_version b/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}_version
new file mode 100644
index 0000000..6e8bf73
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/src/main/rhdzmota/ext/{{cookiecutter.project_slug}}_version
@@ -0,0 +1,1 @@
+0.1.0
diff --git a/requirements-develop.txt b/requirements-develop.txt
index 5af0a3a..14ba2be 100644
--- a/requirements-develop.txt
+++ b/requirements-develop.txt
@@ -1,6 +1,7 @@
 pycodestyle==2.7.0
 pytest==7.4.2
 coverage==7.3.2
+cookiecutter==2.4.0
 mypy==0.971
 mypy-extensions==0.4.3
 typing_extensions==4.3.0


Test patch:
diff --git a/extensions/{{cookiecutter.project_slug}}/src/test/test_rhdzmota/test_ext/test_{{cookiecutter.project_slug}}/__init__.py b/extensions/{{cookiecutter.project_slug}}/src/test/test_rhdzmota/test_ext/test_{{cookiecutter.project_slug}}/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/extensions/{{cookiecutter.project_slug}}/src/test/test_rhdzmota/test_ext/test_{{cookiecutter.project_slug}}/test_settings.py b/extensions/{{cookiecutter.project_slug}}/src/test/test_rhdzmota/test_ext/test_{{cookiecutter.project_slug}}/test_settings.py
new file mode 100644
index 0000000..3779fcc
--- /dev/null
+++ b/extensions/{{cookiecutter.project_slug}}/src/test/test_rhdzmota/test_ext/test_{{cookiecutter.project_slug}}/test_settings.py
@@ -0,0 +1,6 @@
+from rhdzmota.ext.{{cookiecutter.project_slug}}.settings import Env
+
+
+def test_env_app_name():
+    app_name = "{{cookiecutter.project_slug}}"
+    assert Env.EXT_APP_NAME.startswith(app_name)