datamodel-code-generator icon indicating copy to clipboard operation
datamodel-code-generator copied to clipboard

Incorrect Package-Relative Import for Schemas Contained Within a Namespace

Open willsawyerrrr opened this issue 7 months ago • 0 comments

Describe the bug

Given two schemas which are contained within a namespace, when a schema at the top level of the namespace references a schema from a sub-namespace, the relative import from the sibling module is one level too far up the parent package heirarchy - from .. import subns instead of from . import subns.

Namespace?

I am using namespaces to refer to the .-delimited leading components of a schema's name; for example, my_ns.foo and my_ns.bar are contained within the my_ns namespace and my_ns.my_sub_ns.baz is contained within the my_sub_ns sub-namespace of the my_ns namespace.

To Reproduce

Example schema:

// openapi.json

{
  "openapi": "3.0.1",
  "components": {
    "schemas": {
      "ns.wrapper": {
        "type": "object",
        "properties": {
          "Item": {
            "$ref": "#/components/schemas/ns.subns.item"
          }
        }
      },
      "ns.subns.item": {
        "type": "object",
        "properties": {
          "Type": {
            "type": "string"
          }
        }
      }
    }
  }
}

Used commandline:

$ datamodel-codegen --input openapi.json --input-file-type openapi --output schemas

Produces the following file structure

schemas
|-- __init__.py
`-- ns
    |-- __init__.py
    `-- subns.py

with the following contents

# schemas/ns/__init__.py (truncated)

from .. import subns

class Wrapper(BaseModel):
    Item: Optional[subns.Item] = None
# schemas/ns/subns.py (truncated)

class Item(BaseModel):
    Type: Optional[str] = None

Expected behavior

The relative import should be equivalent to the corresponding case where the schemas are not contained within a namespace.

Given the following change to the input file

// openapi.json

{
  "openapi": "3.0.1",
   "components": {
     "schemas": {
-      "ns.wrapper": {
+      "wrapper": {
         "type": "object",
         "properties": {
           "Item": {
-            "$ref": "#/components/schemas/ns.subns.item"
+            "$ref": "#/components/schemas/subns.item"
           }
         }
       },
-      "ns.subns.item": {
+      "subns.item": {
         "type": "object",
         "properties": {
           "Type": {
            "type": "string"
          }
        }
      }
    }
  }
}

the following changes are observed:

Using the same command line produces the following file structure

schemas
|-- __init__.py
- `-- ns
-     |-- __init__.py
-     `-- subns.py
+ `-- subns.py

with the following contents

# schemas/__init__.py (previously schemas/ns/__init__.py) (truncated)

- from .. import subns
+ from . import subns

class Wrapper(BaseModel):
    Item: Optional[subns.Item] = None
# schemas/subns.py (previously schemas/ns/subns.py) (truncated)

class Item(BaseModel):
    Type: Optional[str] = None

This change reflects the expected result for the nested case.

Version:

  • OS: macOS
  • Python version: 3.12.9
  • datamodel-code-generator version: 0.28.1

willsawyerrrr avatar Mar 28 '25 14:03 willsawyerrrr