# HG changeset patch
# User Vincent Hatakeyama <vincent.hatakeyama@xcg-consulting.fr>
# Date 1690442778 -32400
#      Thu Jul 27 16:26:18 2023 +0900
# Node ID 2cceb1f6936174cb833971dc614f2dca4be00a81
# Parent  64c60365beac7fb91dab0be6074c7490f7addf9d
✨ give a way to set up multiple mount/target directory in a single python package

Also add a workaround a hatchling/setuptools-scm bug.

diff --git a/NEWS.rst b/NEWS.rst
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -2,6 +2,16 @@
 History
 =======
 
+20.7.1
+------
+
+Fix typing error.
+
+20.7.0
+------
+
+Change python_packages keys to allow several mount point and targets in the same package).
+
 20.6.0
 ------
 
diff --git a/odoo_scripts/config.py b/odoo_scripts/config.py
--- a/odoo_scripts/config.py
+++ b/odoo_scripts/config.py
@@ -8,7 +8,7 @@
 from ast import literal_eval
 from collections import OrderedDict, defaultdict
 from glob import glob
-from typing import Any, Dict, List, Optional, Union
+from typing import Any, Callable, Dict, List, Optional, Tuple, Union
 
 try:
     # Python 3.11+
@@ -104,10 +104,8 @@
             if "path" not in value:
                 _logger.error("Expansion without path for %s", name)
                 raise Exception("Expansion without path for %s" % name)
-            # put in relative_path the relative path between this and the
-            # parent.
-            # this allows blacklisting odoo-mint/theme when expanding
-            # mint hr.
+            # put in relative_path the relative path between this and the parent.
+            # this allows blacklisting odoo-mint/theme when expanding mint hr.
             if parent_configuration:
                 # difference between this path and the parents
                 relative_path = os.path.join(
@@ -174,7 +172,50 @@
                 )
             setattr(self, key, list(set_values))
 
-        self.python_packages: Dict[str, Dict[str, str]]
+        def read_expanded(
+            key: str,
+            default_value=None,
+            value_formatter: Optional[Callable[[str], Any]] = None,
+        ):
+            """Read from local version, or from expanded if value is found.
+            Raise an exception if more than one value is found."""
+            value = default_value
+            if toread(key):
+                found = False
+                for config in self._expanded_configuration.values():
+                    if hasattr(config, key):
+                        if found:
+                            raise Exception(
+                                "Key %s to read in several expanded "
+                                "configurations" % key
+                            )
+                        value = getattr(config, key)
+                        found = True
+                if key in section:
+                    local_value = section.get(key, default_value)
+                    if value_formatter is not None:
+                        local_value = value_formatter(local_value)
+                    if found and value != default_value and local_value != value:
+                        raise Exception(
+                            "Key %s in expanded configuration and locally set"
+                            " with different values" % key
+                        )
+                    value = local_value
+            if toread(key):
+                setattr(self, key, value)
+
+        def version_formatter(value: str) -> Tuple[int, int, int]:
+            version_tuple = value.split(".")
+            return (
+                int(version_tuple[0]) if len(version_tuple) >= 1 else 0,
+                int(version_tuple[1]) if len(version_tuple) >= 2 else 0,
+                int(version_tuple[2]) if len(version_tuple) >= 3 else 0,
+            )
+
+        self.min_version: Tuple[int, int, int]
+        read_expanded("min_version", (0, 0, 0), version_formatter)
+
+        self.python_packages: Dict[str, Dict[str, Union[str, List[Tuple[str, str]]]]]
         if toread("python_packages"):
             self.python_packages = OrderedDict()
             # add any expanded values
@@ -188,11 +229,42 @@
                 section.get("python_packages", "{}")
             ).items():
                 value_with_default = {
-                    "mount": ".",
-                    "target": os.path.join("odoo", "addons", os.path.basename(key)),
-                    "compile": "True",
+                    "dirs": [],
+                    "compile": value.get("compile", "True"),
                 }
-                value_with_default.update(value)
+                # previously authorized keys.
+                if "mount" in value or "target" in value:
+                    simple_options = (
+                        value.get("mount", "."),
+                        value.get(
+                            "target",
+                            os.path.join("odoo", "addons", os.path.basename(key)),
+                        ),
+                    )
+                    value_with_default["dirs"].append(simple_options)
+                if "dirs" in value:
+                    for element in value["dirs"]:
+                        value_with_default["dirs"].append(
+                            (
+                                element.get("mount", "."),
+                                element.get(
+                                    "target",
+                                    os.path.join(
+                                        "odoo",
+                                        "addons",
+                                        os.path.basename(
+                                            element["mount"]
+                                            if "mount" in element
+                                            else key
+                                        ),
+                                    ),
+                                ),
+                            )
+                        )
+                elif "mount" not in value and "target" not in value:
+                    value_with_default["dirs"].append(
+                        (".", os.path.join("odoo", "addons", os.path.basename(key)))
+                    )
                 self.python_packages[key] = value_with_default
 
         self.addons_path = {os.path.dirname(module) for module in self.modules}
@@ -232,32 +304,6 @@
         ):
             setattr(self, key_format(key), section.get(key, None))
 
-        def read_expanded(key: str, default_value=None):
-            """Read from local version, or from expanded if value is found.
-            Raise an exception if more than one value is found."""
-            value = default_value
-            if toread(key):
-                found = False
-                for config in self._expanded_configuration.values():
-                    if hasattr(config, key):
-                        if found:
-                            raise Exception(
-                                "Key %s to read in several expanded "
-                                "configurations" % key
-                            )
-                        value = getattr(config, key)
-                        found = True
-                if key in section:
-                    local_value = section.get(key, default_value)
-                    if found and value != default_value and local_value != value:
-                        raise Exception(
-                            "Key %s in expanded configuration and locally set"
-                            " with different values" % key
-                        )
-                    value = local_value
-            if toread(key):
-                setattr(self, key, value)
-
         self.registry: Optional[str] = (
             section.get("registry", "registry.xcg.io") if registry is None else registry
         )
diff --git a/odoo_scripts/docker_build_copy.py b/odoo_scripts/docker_build_copy.py
--- a/odoo_scripts/docker_build_copy.py
+++ b/odoo_scripts/docker_build_copy.py
@@ -129,6 +129,17 @@
                 )
                 client = hglib.open(cvs_parent)
                 client.archive(this_package_target.encode("UTF-8"))
+                # fix for setuptools-scm/hatchling that do not like when the file has
+                # latesttag: null
+                check_call(
+                    [
+                        "sed",
+                        "-i",
+                        "-s",
+                        "s,latesttag: null,latesttag: 0,",
+                        os.path.join(this_package_target, ".hg_archival.txt"),
+                    ]
+                )
             else:
                 _copy_package(cvs_parent + os.path.sep, this_package_target)
             requirements.append(
diff --git a/odoo_scripts/docker_client.py b/odoo_scripts/docker_client.py
--- a/odoo_scripts/docker_client.py
+++ b/odoo_scripts/docker_client.py
@@ -256,12 +256,13 @@
 
     _logger.debug("python packages: %s", ",".join(config.python_packages))
     for python_package in config.python_packages.items():
-        full_target_path = os.path.join(base_python_path, python_package[1]["target"])
-        full_project_path = os.path.realpath(
-            os.path.join(project_path, python_package[0], python_package[1]["mount"])
-        )
-        mount_list.append(Mount(full_target_path, full_project_path, "bind"))
-        mount_dict[full_target_path] = full_project_path
+        for dir_ in python_package[1]["dirs"]:
+            full_target_path = os.path.join(base_python_path, dir_[1])
+            full_project_path = os.path.realpath(
+                os.path.join(project_path, python_package[0], dir_[0])
+            )
+            mount_list.append(Mount(full_target_path, full_project_path, "bind"))
+            mount_dict[full_target_path] = full_project_path
 
     return mount_list, mount_dict