diff --git a/docs/cli-reference/index.md b/docs/cli-reference/index.md index f7a3d7886..739d4dda3 100644 --- a/docs/cli-reference/index.md +++ b/docs/cli-reference/index.md @@ -112,6 +112,7 @@ This documentation is auto-generated from test cases. - [`--no-alias`](field-customization.md#no-alias) - [`--no-color`](utility-options.md#no-color) +- [`--no-treat-dot-as-module`](template-customization.md#no-treat-dot-as-module) - [`--no-use-specialized-enum`](typing-customization.md#no-use-specialized-enum) - [`--no-use-standard-collections`](typing-customization.md#no-use-standard-collections) - [`--no-use-union-operator`](typing-customization.md#no-use-union-operator) @@ -150,7 +151,6 @@ This documentation is auto-generated from test cases. ### T {#t} - [`--target-python-version`](model-customization.md#target-python-version) -- [`--treat-dot-as-module`](template-customization.md#treat-dot-as-module) - [`--type-mappings`](typing-customization.md#type-mappings) ### U {#u} diff --git a/docs/cli-reference/quick-reference.md b/docs/cli-reference/quick-reference.md index c8b2b343c..ffca85484 100644 --- a/docs/cli-reference/quick-reference.md +++ b/docs/cli-reference/quick-reference.md @@ -119,7 +119,7 @@ datamodel-codegen [OPTIONS] | [`--enable-version-header`](template-customization.md#enable-version-header) | Include tool version information in file header. | | [`--extra-template-data`](template-customization.md#extra-template-data) | Pass custom template variables from JSON file for code generation. | | [`--formatters`](template-customization.md#formatters) | Specify code formatters to apply to generated output. | -| [`--treat-dot-as-module`](template-customization.md#treat-dot-as-module) | Treat dots in schema names as module separators. | +| [`--no-treat-dot-as-module`](template-customization.md#no-treat-dot-as-module) | Keep dots in schema names as underscores for flat output. | | [`--use-double-quotes`](template-customization.md#use-double-quotes) | Use double quotes for string literals in generated code. | | [`--use-exact-imports`](template-customization.md#use-exact-imports) | Import exact types instead of modules. | | [`--wrap-string-literal`](template-customization.md#wrap-string-literal) | Wrap long string literals across multiple lines. | @@ -225,6 +225,7 @@ All options sorted alphabetically: - [`--module-split-mode`](general-options.md#module-split-mode) - Split generated models into separate files, one per model cl... - [`--no-alias`](field-customization.md#no-alias) - Disable Field alias generation for non-Python-safe property ... - [`--no-color`](utility-options.md#no-color) - Disable colorized output +- [`--no-treat-dot-as-module`](template-customization.md#no-treat-dot-as-module) - Keep dots in schema names as underscores for flat output. - [`--no-use-specialized-enum`](typing-customization.md#no-use-specialized-enum) - Disable specialized Enum classes for Python 3.11+ code gener... - [`--no-use-standard-collections`](typing-customization.md#no-use-standard-collections) - Use built-in dict/list instead of typing.Dict/List. - [`--no-use-union-operator`](typing-customization.md#no-use-union-operator) - Test GraphQL annotated types with standard collections and u... @@ -248,7 +249,6 @@ All options sorted alphabetically: - [`--strict-types`](typing-customization.md#strict-types) - Enable strict type validation for specified Python types. - [`--strip-default-none`](model-customization.md#strip-default-none) - Remove fields with None as default value from generated mode... - [`--target-python-version`](model-customization.md#target-python-version) - Target Python version for generated code syntax and imports. -- [`--treat-dot-as-module`](template-customization.md#treat-dot-as-module) - Treat dots in schema names as module separators. - [`--type-mappings`](typing-customization.md#type-mappings) - Override default type mappings for schema formats. - [`--union-mode`](model-customization.md#union-mode) - Union mode for combining anyOf/oneOf schemas (smart or left_... - [`--url`](base-options.md#url) - Fetch schema from URL with custom HTTP headers. diff --git a/docs/cli-reference/template-customization.md b/docs/cli-reference/template-customization.md index ff23c27f1..2f7d37a22 100644 --- a/docs/cli-reference/template-customization.md +++ b/docs/cli-reference/template-customization.md @@ -16,7 +16,7 @@ | [`--enable-version-header`](#enable-version-header) | Include tool version information in file header. | | [`--extra-template-data`](#extra-template-data) | Pass custom template variables from JSON file for code gener... | | [`--formatters`](#formatters) | Specify code formatters to apply to generated output. | -| [`--treat-dot-as-module`](#treat-dot-as-module) | Treat dots in schema names as module separators. | +| [`--no-treat-dot-as-module`](#no-treat-dot-as-module) | Keep dots in schema names as underscores for flat output. | | [`--use-double-quotes`](#use-double-quotes) | Use double quotes for string literals in generated code. | | [`--use-exact-imports`](#use-exact-imports) | Import exact types instead of modules. | | [`--wrap-string-literal`](#wrap-string-literal) | Wrap long string literals across multiple lines. | @@ -2260,21 +2260,21 @@ Use this to customize formatting or disable formatters entirely. --- -## `--treat-dot-as-module` {#treat-dot-as-module} +## `--no-treat-dot-as-module` {#no-treat-dot-as-module} -Treat dots in schema names as module separators. +Keep dots in schema names as underscores for flat output. -The `--treat-dot-as-module` flag configures the code generation behavior. +The `--no-treat-dot-as-module` flag prevents splitting dotted schema names. **See also:** [Module Structure and Exports](../module-exports.md) !!! tip "Usage" ```bash - datamodel-codegen --input schema.json --treat-dot-as-module # (1)! + datamodel-codegen --input schema.json --no-treat-dot-as-module # (1)! ``` - 1. :material-arrow-left: `--treat-dot-as-module` - the option documented here + 1. :material-arrow-left: `--no-treat-dot-as-module` - the option documented here ??? example "Examples" @@ -2306,12 +2306,7 @@ The `--treat-dot-as-module` flag configures the code generation behavior. # filename: treat_dot_as_module_single # timestamp: 2019-07-26T00:00:00+00:00 - # model/__init__.py - # generated by datamodel-codegen: - # filename: treat_dot_as_module_single - # timestamp: 2019-07-26T00:00:00+00:00 - - # model/schema.py + # model_schema.py # generated by datamodel-codegen: # filename: model.schema.json # timestamp: 2019-07-26T00:00:00+00:00 diff --git a/src/datamodel_code_generator/__init__.py b/src/datamodel_code_generator/__init__.py index 3008dfb09..e7d5ce186 100644 --- a/src/datamodel_code_generator/__init__.py +++ b/src/datamodel_code_generator/__init__.py @@ -465,7 +465,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915 custom_formatters_kwargs: dict[str, Any] | None = None, use_pendulum: bool = False, http_query_parameters: Sequence[tuple[str, str]] | None = None, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, use_exact_imports: bool = False, union_mode: UnionMode | None = None, output_datetime_class: DatetimeClassType | None = None, diff --git a/src/datamodel_code_generator/__main__.py b/src/datamodel_code_generator/__main__.py index fb2791d2e..03928ecb3 100644 --- a/src/datamodel_code_generator/__main__.py +++ b/src/datamodel_code_generator/__main__.py @@ -452,7 +452,7 @@ def validate_all_exports_collision_strategy(cls, values: dict[str, Any]) -> dict custom_formatters_kwargs: Optional[TextIOBase] = None # noqa: UP045 use_pendulum: bool = False http_query_parameters: Optional[Sequence[tuple[str, str]]] = None # noqa: UP045 - treat_dot_as_module: bool = False + treat_dot_as_module: Optional[bool] = None # noqa: UP045 use_exact_imports: bool = False union_mode: Optional[UnionMode] = None # noqa: UP045 output_datetime_class: Optional[DatetimeClassType] = None # noqa: UP045 diff --git a/src/datamodel_code_generator/arguments.py b/src/datamodel_code_generator/arguments.py index 9cedb2ef6..464ba5408 100644 --- a/src/datamodel_code_generator/arguments.py +++ b/src/datamodel_code_generator/arguments.py @@ -272,8 +272,9 @@ def start_section(self, heading: str | None) -> None: ) model_options.add_argument( "--treat-dot-as-module", - help="treat dotted module names as modules", - action="store_true", + help="Treat dotted schema names as module paths, creating nested directory structures (e.g., 'foo.bar.Model' " + "becomes 'foo/bar.py'). Use --no-treat-dot-as-module to keep dots in names as underscores for single-file output.", + action=BooleanOptionalAction, default=None, ) model_options.add_argument( diff --git a/src/datamodel_code_generator/cli_options.py b/src/datamodel_code_generator/cli_options.py index fc86244e8..9161747e4 100644 --- a/src/datamodel_code_generator/cli_options.py +++ b/src/datamodel_code_generator/cli_options.py @@ -177,7 +177,7 @@ class CLIOptionMeta: "--disable-appending-item-suffix": CLIOptionMeta( name="--disable-appending-item-suffix", category=OptionCategory.TEMPLATE ), - "--treat-dot-as-module": CLIOptionMeta(name="--treat-dot-as-module", category=OptionCategory.TEMPLATE), + "--no-treat-dot-as-module": CLIOptionMeta(name="--no-treat-dot-as-module", category=OptionCategory.TEMPLATE), "--disable-timestamp": CLIOptionMeta(name="--disable-timestamp", category=OptionCategory.TEMPLATE), "--enable-version-header": CLIOptionMeta(name="--enable-version-header", category=OptionCategory.TEMPLATE), "--enable-command-header": CLIOptionMeta(name="--enable-command-header", category=OptionCategory.TEMPLATE), diff --git a/src/datamodel_code_generator/model/base.py b/src/datamodel_code_generator/model/base.py index 47a7f7467..a44c57253 100644 --- a/src/datamodel_code_generator/model/base.py +++ b/src/datamodel_code_generator/model/base.py @@ -406,8 +406,12 @@ def get_template(template_file_path: Path) -> Template: return environment.get_template(template_file_path.name) -def sanitize_module_name(name: str, *, treat_dot_as_module: bool) -> str: - """Sanitize a module name by replacing invalid characters.""" +def sanitize_module_name(name: str, *, treat_dot_as_module: bool | None) -> str: + """Sanitize a module name by replacing invalid characters. + + If treat_dot_as_module is True, dots are preserved in the name. + If treat_dot_as_module is False or None (default), dots are replaced with underscores. + """ pattern = r"[^0-9a-zA-Z_.]" if treat_dot_as_module else r"[^0-9a-zA-Z_]" sanitized = re.sub(pattern, "_", name) if sanitized and sanitized[0].isdigit(): @@ -415,19 +419,28 @@ def sanitize_module_name(name: str, *, treat_dot_as_module: bool) -> str: return sanitized -def get_module_path(name: str, file_path: Path | None, *, treat_dot_as_module: bool) -> list[str]: - """Get the module path components from a name and file path.""" +def get_module_path(name: str, file_path: Path | None, *, treat_dot_as_module: bool | None) -> list[str]: + """Get the module path components from a name and file path. + + The treat_dot_as_module flag controls behavior: + - None (default): Split names on dots (backward compat), but sanitize file names (replace dots) + - True: Split names on dots AND keep dots in file names (for modular output) + - False: Don't split names on dots AND sanitize file names (new feature for flat output) + """ + should_split_names = treat_dot_as_module is not False + should_keep_dots_in_files = treat_dot_as_module is True if file_path: - sanitized_stem = sanitize_module_name(file_path.stem, treat_dot_as_module=treat_dot_as_module) + sanitized_stem = sanitize_module_name(file_path.stem, treat_dot_as_module=should_keep_dots_in_files) + module_parts = name.split(".")[:-1] if should_split_names else [] return [ *file_path.parts[:-1], sanitized_stem, - *name.split(".")[:-1], + *module_parts, ] - return name.split(".")[:-1] + return name.split(".")[:-1] if should_split_names else [] -def get_module_name(name: str, file_path: Path | None, *, treat_dot_as_module: bool) -> str: +def get_module_name(name: str, file_path: Path | None, *, treat_dot_as_module: bool | None) -> str: """Get the full module name from a name and file path.""" return ".".join(get_module_path(name, file_path, treat_dot_as_module=treat_dot_as_module)) @@ -501,7 +514,7 @@ def __init__( # noqa: PLR0913 nullable: bool = False, keyword_only: bool = False, frozen: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, dataclass_arguments: DataclassArguments | None = None, ) -> None: """Initialize a data model with fields, base classes, and configuration.""" @@ -561,7 +574,7 @@ def __init__( # noqa: PLR0913 self._additional_imports.extend(self.DEFAULT_IMPORTS) self.default: Any = default self._nullable: bool = nullable - self._treat_dot_as_module: bool = treat_dot_as_module + self._treat_dot_as_module: bool | None = treat_dot_as_module def _validate_fields(self, fields: list[DataModelFieldBase]) -> list[DataModelFieldBase]: names: set[str] = set() @@ -721,7 +734,7 @@ def create_base_class_model( reference: Reference, # noqa: ARG003 custom_template_dir: Path | None = None, # noqa: ARG003 keyword_only: bool = False, # noqa: ARG003, FBT001, FBT002 - treat_dot_as_module: bool = False, # noqa: ARG003, FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: ARG003, FBT001 ) -> DataModel | None: """Create a shared base class model for DRY configuration. diff --git a/src/datamodel_code_generator/model/dataclass.py b/src/datamodel_code_generator/model/dataclass.py index 545b6c42c..836313c2e 100644 --- a/src/datamodel_code_generator/model/dataclass.py +++ b/src/datamodel_code_generator/model/dataclass.py @@ -62,7 +62,7 @@ def __init__( # noqa: PLR0913 nullable: bool = False, keyword_only: bool = False, frozen: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, dataclass_arguments: DataclassArguments | None = None, ) -> None: """Initialize dataclass with fields sorted by field assignment requirement.""" @@ -218,7 +218,7 @@ def __init__( # noqa: PLR0913, PLR0917 use_union_operator: bool = False, # noqa: FBT001, FBT002 use_pendulum: bool = False, # noqa: FBT001, FBT002 target_datetime_class: DatetimeClassType = DatetimeClassType.Datetime, - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 use_serialize_as_any: bool = False, # noqa: FBT001, FBT002 ) -> None: """Initialize type manager with datetime type mapping.""" diff --git a/src/datamodel_code_generator/model/enum.py b/src/datamodel_code_generator/model/enum.py index fda352d12..403ab599b 100644 --- a/src/datamodel_code_generator/model/enum.py +++ b/src/datamodel_code_generator/model/enum.py @@ -61,7 +61,7 @@ def __init__( # noqa: PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize Enum with optional specialized base class based on type.""" super().__init__( diff --git a/src/datamodel_code_generator/model/msgspec.py b/src/datamodel_code_generator/model/msgspec.py index e6d9ac1ed..47f01a4ad 100644 --- a/src/datamodel_code_generator/model/msgspec.py +++ b/src/datamodel_code_generator/model/msgspec.py @@ -140,7 +140,7 @@ def __init__( # noqa: PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize msgspec Struct with fields sorted by field assignment requirement.""" super().__init__( @@ -174,7 +174,7 @@ def create_base_class_model( reference: Reference, custom_template_dir: Path | None = None, keyword_only: bool = False, # noqa: FBT001, FBT002 - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 ) -> Struct | None: """Create a shared base class model for DRY configuration. @@ -504,7 +504,7 @@ def __init__( # noqa: PLR0913, PLR0917 use_union_operator: bool = False, # noqa: FBT001, FBT002 use_pendulum: bool = False, # noqa: FBT001, FBT002 target_datetime_class: DatetimeClassType | None = None, - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 use_serialize_as_any: bool = False, # noqa: FBT001, FBT002 ) -> None: """Initialize type manager with optional datetime type mapping.""" diff --git a/src/datamodel_code_generator/model/pydantic/base_model.py b/src/datamodel_code_generator/model/pydantic/base_model.py index 57d88711c..b81b5ecbe 100644 --- a/src/datamodel_code_generator/model/pydantic/base_model.py +++ b/src/datamodel_code_generator/model/pydantic/base_model.py @@ -288,7 +288,7 @@ def __init__( # noqa: PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize the BaseModel with fields and configuration.""" methods: list[str] = [field.method for field in fields if field.method] @@ -345,7 +345,7 @@ def __init__( # noqa: PLR0912, PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize the BaseModel with Config and extra fields support.""" super().__init__( diff --git a/src/datamodel_code_generator/model/pydantic/types.py b/src/datamodel_code_generator/model/pydantic/types.py index 69c021ec8..c2c1edb02 100644 --- a/src/datamodel_code_generator/model/pydantic/types.py +++ b/src/datamodel_code_generator/model/pydantic/types.py @@ -181,7 +181,7 @@ def __init__( # noqa: PLR0913, PLR0917 use_union_operator: bool = False, # noqa: FBT001, FBT002 use_pendulum: bool = False, # noqa: FBT001, FBT002 target_datetime_class: DatetimeClassType | None = None, - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 use_serialize_as_any: bool = False, # noqa: FBT001, FBT002 ) -> None: """Initialize the DataTypeManager with Pydantic v1 type mappings.""" diff --git a/src/datamodel_code_generator/model/pydantic_v2/base_model.py b/src/datamodel_code_generator/model/pydantic_v2/base_model.py index 97e8d0db7..f2d267e06 100644 --- a/src/datamodel_code_generator/model/pydantic_v2/base_model.py +++ b/src/datamodel_code_generator/model/pydantic_v2/base_model.py @@ -200,7 +200,7 @@ def __init__( # noqa: PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize BaseModel with ConfigDict generation from template data.""" super().__init__( @@ -285,7 +285,7 @@ def create_base_class_model( reference: Reference, custom_template_dir: Path | None = None, keyword_only: bool = False, # noqa: FBT001, FBT002 - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 ) -> BaseModel | None: """Create a shared base class model for DRY configuration. diff --git a/src/datamodel_code_generator/model/pydantic_v2/types.py b/src/datamodel_code_generator/model/pydantic_v2/types.py index 7a3bba049..3b0a7a6e0 100644 --- a/src/datamodel_code_generator/model/pydantic_v2/types.py +++ b/src/datamodel_code_generator/model/pydantic_v2/types.py @@ -80,7 +80,7 @@ def __init__( # noqa: PLR0913, PLR0917 use_union_operator: bool = False, # noqa: FBT001, FBT002 use_pendulum: bool = False, # noqa: FBT001, FBT002 target_datetime_class: DatetimeClassType | None = None, - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 use_serialize_as_any: bool = False, # noqa: FBT001, FBT002 ) -> None: """Initialize with pydantic v2-specific DataType.""" diff --git a/src/datamodel_code_generator/model/scalar.py b/src/datamodel_code_generator/model/scalar.py index 9fa9e58c8..927088fc7 100644 --- a/src/datamodel_code_generator/model/scalar.py +++ b/src/datamodel_code_generator/model/scalar.py @@ -57,7 +57,7 @@ def __init__( # noqa: PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize GraphQL scalar type with Python type mapping.""" extra_template_data = extra_template_data or defaultdict(dict) diff --git a/src/datamodel_code_generator/model/typed_dict.py b/src/datamodel_code_generator/model/typed_dict.py index 73ac45a70..7ec425af4 100644 --- a/src/datamodel_code_generator/model/typed_dict.py +++ b/src/datamodel_code_generator/model/typed_dict.py @@ -67,7 +67,7 @@ def __init__( # noqa: PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize TypedDict model.""" super().__init__( diff --git a/src/datamodel_code_generator/model/types.py b/src/datamodel_code_generator/model/types.py index 05aad2ca4..ed4a41053 100644 --- a/src/datamodel_code_generator/model/types.py +++ b/src/datamodel_code_generator/model/types.py @@ -84,7 +84,7 @@ def __init__( # noqa: PLR0913, PLR0917 use_union_operator: bool = False, # noqa: FBT001, FBT002 use_pendulum: bool = False, # noqa: FBT001, FBT002 target_datetime_class: DatetimeClassType | None = None, - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 use_serialize_as_any: bool = False, # noqa: FBT001, FBT002 ) -> None: """Initialize type manager with basic type mapping.""" diff --git a/src/datamodel_code_generator/model/union.py b/src/datamodel_code_generator/model/union.py index c0a119ae5..e24d94144 100644 --- a/src/datamodel_code_generator/model/union.py +++ b/src/datamodel_code_generator/model/union.py @@ -42,7 +42,7 @@ def __init__( # noqa: PLR0913 default: Any = UNDEFINED, nullable: bool = False, keyword_only: bool = False, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, ) -> None: """Initialize GraphQL union type.""" super().__init__( diff --git a/src/datamodel_code_generator/parser/base.py b/src/datamodel_code_generator/parser/base.py index 253a2ea51..169065c58 100644 --- a/src/datamodel_code_generator/parser/base.py +++ b/src/datamodel_code_generator/parser/base.py @@ -756,7 +756,7 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915 custom_formatters_kwargs: dict[str, Any] | None = None, use_pendulum: bool = False, http_query_parameters: Sequence[tuple[str, str]] | None = None, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, use_exact_imports: bool = False, default_field_extras: dict[str, Any] | None = None, target_datetime_class: DatetimeClassType | None = None, @@ -784,7 +784,7 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915 use_union_operator=use_union_operator, use_pendulum=use_pendulum, target_datetime_class=target_datetime_class, - treat_dot_as_module=treat_dot_as_module, + treat_dot_as_module=treat_dot_as_module or False, use_serialize_as_any=use_serialize_as_any, ) self.data_model_type: type[DataModel] = data_model_type diff --git a/src/datamodel_code_generator/parser/graphql.py b/src/datamodel_code_generator/parser/graphql.py index 3ee1974d7..5e7ba4161 100644 --- a/src/datamodel_code_generator/parser/graphql.py +++ b/src/datamodel_code_generator/parser/graphql.py @@ -178,7 +178,7 @@ def __init__( # noqa: PLR0913 custom_formatters_kwargs: dict[str, Any] | None = None, use_pendulum: bool = False, http_query_parameters: Sequence[tuple[str, str]] | None = None, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, use_exact_imports: bool = False, default_field_extras: dict[str, Any] | None = None, target_datetime_class: DatetimeClassType = DatetimeClassType.Datetime, diff --git a/src/datamodel_code_generator/parser/jsonschema.py b/src/datamodel_code_generator/parser/jsonschema.py index 8e69d3a50..601841537 100644 --- a/src/datamodel_code_generator/parser/jsonschema.py +++ b/src/datamodel_code_generator/parser/jsonschema.py @@ -591,7 +591,7 @@ def __init__( # noqa: PLR0913 custom_formatters_kwargs: dict[str, Any] | None = None, use_pendulum: bool = False, http_query_parameters: Sequence[tuple[str, str]] | None = None, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, use_exact_imports: bool = False, default_field_extras: dict[str, Any] | None = None, target_datetime_class: DatetimeClassType | None = None, diff --git a/src/datamodel_code_generator/parser/openapi.py b/src/datamodel_code_generator/parser/openapi.py index 9d9511421..c7dc601a5 100644 --- a/src/datamodel_code_generator/parser/openapi.py +++ b/src/datamodel_code_generator/parser/openapi.py @@ -262,7 +262,7 @@ def __init__( # noqa: PLR0913 custom_formatters_kwargs: dict[str, Any] | None = None, use_pendulum: bool = False, http_query_parameters: Sequence[tuple[str, str]] | None = None, - treat_dot_as_module: bool = False, + treat_dot_as_module: bool | None = None, use_exact_imports: bool = False, default_field_extras: dict[str, Any] | None = None, target_datetime_class: DatetimeClassType | None = None, diff --git a/src/datamodel_code_generator/reference.py b/src/datamodel_code_generator/reference.py index e6b944a9c..a420284b8 100644 --- a/src/datamodel_code_generator/reference.py +++ b/src/datamodel_code_generator/reference.py @@ -515,7 +515,7 @@ def __init__( # noqa: PLR0913, PLR0917 no_alias: bool = False, # noqa: FBT001, FBT002 remove_suffix_number: bool = False, # noqa: FBT001, FBT002 parent_scoped_naming: bool = False, # noqa: FBT001, FBT002 - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 ) -> None: """Initialize model resolver with naming and resolution options.""" self.references: dict[str, Reference] = {} @@ -854,7 +854,7 @@ def get_class_name( singular_name_suffix: str | None = None, ) -> ClassName: """Generate a unique class name with optional singularization.""" - if "." in name: + if "." in name and self.treat_dot_as_module is not False: split_name = name.split(".") prefix = ".".join( # TODO: create a validate for class name @@ -865,7 +865,7 @@ def get_class_name( class_name = split_name[-1] else: prefix = "" - class_name = name + class_name = name.replace(".", "_") if "." in name else name class_name = self.class_name_generator(class_name) diff --git a/src/datamodel_code_generator/types.py b/src/datamodel_code_generator/types.py index eca9192b7..e1a6340f4 100644 --- a/src/datamodel_code_generator/types.py +++ b/src/datamodel_code_generator/types.py @@ -825,7 +825,7 @@ def __init__( # noqa: PLR0913, PLR0917 use_union_operator: bool = False, # noqa: FBT001, FBT002 use_pendulum: bool = False, # noqa: FBT001, FBT002 target_datetime_class: DatetimeClassType | None = None, - treat_dot_as_module: bool = False, # noqa: FBT001, FBT002 + treat_dot_as_module: bool | None = None, # noqa: FBT001 use_serialize_as_any: bool = False, # noqa: FBT001, FBT002 ) -> None: """Initialize DataTypeManager with code generation options.""" @@ -840,7 +840,7 @@ def __init__( # noqa: PLR0913, PLR0917 self.use_union_operator: bool = use_union_operator self.use_pendulum: bool = use_pendulum self.target_datetime_class: DatetimeClassType | None = target_datetime_class - self.treat_dot_as_module: bool = treat_dot_as_module + self.treat_dot_as_module: bool = treat_dot_as_module or False self.use_serialize_as_any: bool = use_serialize_as_any self.data_type: type[DataType] = create_model( diff --git a/tests/data/expected/main/jsonschema/no_treat_dot_single/__init__.py b/tests/data/expected/main/jsonschema/no_treat_dot_single/__init__.py new file mode 100644 index 000000000..dd8f594f7 --- /dev/null +++ b/tests/data/expected/main/jsonschema/no_treat_dot_single/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: no_treat_dot_single +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/no_treat_dot_single/v0_0_39_job.py b/tests/data/expected/main/jsonschema/no_treat_dot_single/v0_0_39_job.py new file mode 100644 index 000000000..72a040866 --- /dev/null +++ b/tests/data/expected/main/jsonschema/no_treat_dot_single/v0_0_39_job.py @@ -0,0 +1,12 @@ +# generated by datamodel-codegen: +# filename: v0.0.39.job.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + + +class Job(BaseModel): + id: int + name: str diff --git a/tests/data/expected/main/jsonschema/treat_dot_as_module_single_no_treat/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_as_module_single_no_treat/__init__.py new file mode 100644 index 000000000..ec0858be4 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_as_module_single_no_treat/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: treat_dot_as_module_single +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_as_module_single_no_treat/model_schema.py b/tests/data/expected/main/jsonschema/treat_dot_as_module_single_no_treat/model_schema.py new file mode 100644 index 000000000..6f026d6b2 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_as_module_single_no_treat/model_schema.py @@ -0,0 +1,12 @@ +# generated by datamodel-codegen: +# filename: model.schema.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + + +class User(BaseModel): + name: str + age: int | None = None diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/__init__.py new file mode 100644 index 000000000..c982e8d9a --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: treat_dot_complex +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/api_v1_address.py b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/api_v1_address.py new file mode 100644 index 000000000..85c0a534b --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/api_v1_address.py @@ -0,0 +1,15 @@ +# generated by datamodel-codegen: +# filename: api.v1.address.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + +from . import common_types_country + + +class Address(BaseModel): + street: str | None = None + city: str | None = None + country: common_types_country.Country | None = None diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/api_v1_user.py b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/api_v1_user.py new file mode 100644 index 000000000..45b5cb0cf --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/api_v1_user.py @@ -0,0 +1,16 @@ +# generated by datamodel-codegen: +# filename: api.v1.user.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + +from . import api_v1_address, common_types_status + + +class User(BaseModel): + id: int + name: str + address: api_v1_address.Address | None = None + status: common_types_status.Status | None = None diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/common_types_country.py b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/common_types_country.py new file mode 100644 index 000000000..d9f50b5e5 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/common_types_country.py @@ -0,0 +1,12 @@ +# generated by datamodel-codegen: +# filename: common.types.country.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel, constr + + +class Country(BaseModel): + code: constr(min_length=2, max_length=2) + name: str diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/common_types_status.py b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/common_types_status.py new file mode 100644 index 000000000..b7f2e792c --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_no_treat/common_types_status.py @@ -0,0 +1,13 @@ +# generated by datamodel-codegen: +# filename: common.types.status.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from enum import Enum + + +class Status(Enum): + active = 'active' + inactive = 'inactive' + pending = 'pending' diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/__init__.py new file mode 100644 index 000000000..c982e8d9a --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: treat_dot_complex +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/__init__.py new file mode 100644 index 000000000..c982e8d9a --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: treat_dot_complex +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/__init__.py new file mode 100644 index 000000000..c982e8d9a --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: treat_dot_complex +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/address.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/address.py new file mode 100644 index 000000000..1ed253941 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/address.py @@ -0,0 +1,15 @@ +# generated by datamodel-codegen: +# filename: api.v1.address.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + +from ...common.types import country as country_1 + + +class Address(BaseModel): + street: str | None = None + city: str | None = None + country: country_1.Country | None = None diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/user.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/user.py new file mode 100644 index 000000000..06bccf5ba --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/api/v1/user.py @@ -0,0 +1,17 @@ +# generated by datamodel-codegen: +# filename: api.v1.user.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + +from ...common.types import status as status_1 +from . import address as address_1 + + +class User(BaseModel): + id: int + name: str + address: address_1.Address | None = None + status: status_1.Status | None = None diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/__init__.py new file mode 100644 index 000000000..c982e8d9a --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: treat_dot_complex +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/__init__.py new file mode 100644 index 000000000..c982e8d9a --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: treat_dot_complex +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/country.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/country.py new file mode 100644 index 000000000..d9f50b5e5 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/country.py @@ -0,0 +1,12 @@ +# generated by datamodel-codegen: +# filename: common.types.country.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel, constr + + +class Country(BaseModel): + code: constr(min_length=2, max_length=2) + name: str diff --git a/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/status.py b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/status.py new file mode 100644 index 000000000..b7f2e792c --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_complex_treat/common/types/status.py @@ -0,0 +1,13 @@ +# generated by datamodel-codegen: +# filename: common.types.status.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from enum import Enum + + +class Status(Enum): + active = 'active' + inactive = 'inactive' + pending = 'pending' diff --git a/tests/data/expected/main/jsonschema/treat_dot_single/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_single/__init__.py new file mode 100644 index 000000000..dd8f594f7 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_single/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: no_treat_dot_single +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/39/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/39/__init__.py new file mode 100644 index 000000000..dd8f594f7 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/39/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: no_treat_dot_single +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/39/job.py b/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/39/job.py new file mode 100644 index 000000000..72a040866 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/39/job.py @@ -0,0 +1,12 @@ +# generated by datamodel-codegen: +# filename: v0.0.39.job.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + + +class Job(BaseModel): + id: int + name: str diff --git a/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/__init__.py new file mode 100644 index 000000000..dd8f594f7 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_single/v0/0/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: no_treat_dot_single +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/expected/main/jsonschema/treat_dot_single/v0/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_single/v0/__init__.py new file mode 100644 index 000000000..dd8f594f7 --- /dev/null +++ b/tests/data/expected/main/jsonschema/treat_dot_single/v0/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: no_treat_dot_single +# timestamp: 2019-07-26T00:00:00+00:00 diff --git a/tests/data/jsonschema/no_treat_dot_single/v0.0.39.job.json b/tests/data/jsonschema/no_treat_dot_single/v0.0.39.job.json new file mode 100644 index 000000000..82f4591b0 --- /dev/null +++ b/tests/data/jsonschema/no_treat_dot_single/v0.0.39.job.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Job", + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name"] +} diff --git a/tests/data/jsonschema/treat_dot_complex/api.v1.address.json b/tests/data/jsonschema/treat_dot_complex/api.v1.address.json new file mode 100644 index 000000000..1fcca8138 --- /dev/null +++ b/tests/data/jsonschema/treat_dot_complex/api.v1.address.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Address", + "type": "object", + "properties": { + "street": { + "type": "string" + }, + "city": { + "type": "string" + }, + "country": { + "$ref": "common.types.country.json" + } + } +} diff --git a/tests/data/jsonschema/treat_dot_complex/api.v1.user.json b/tests/data/jsonschema/treat_dot_complex/api.v1.user.json new file mode 100644 index 000000000..48fd4d406 --- /dev/null +++ b/tests/data/jsonschema/treat_dot_complex/api.v1.user.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "User", + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "address": { + "$ref": "api.v1.address.json" + }, + "status": { + "$ref": "common.types.status.json" + } + }, + "required": ["id", "name"] +} diff --git a/tests/data/jsonschema/treat_dot_complex/common.types.country.json b/tests/data/jsonschema/treat_dot_complex/common.types.country.json new file mode 100644 index 000000000..1f882a45d --- /dev/null +++ b/tests/data/jsonschema/treat_dot_complex/common.types.country.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Country", + "type": "object", + "properties": { + "code": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "name": { + "type": "string" + } + }, + "required": ["code", "name"] +} diff --git a/tests/data/jsonschema/treat_dot_complex/common.types.status.json b/tests/data/jsonschema/treat_dot_complex/common.types.status.json new file mode 100644 index 000000000..3760ff027 --- /dev/null +++ b/tests/data/jsonschema/treat_dot_complex/common.types.status.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Status", + "type": "string", + "enum": ["active", "inactive", "pending"] +} diff --git a/tests/main/jsonschema/test_main_jsonschema.py b/tests/main/jsonschema/test_main_jsonschema.py index 1475e83a3..e183fd4cc 100644 --- a/tests/main/jsonschema/test_main_jsonschema.py +++ b/tests/main/jsonschema/test_main_jsonschema.py @@ -2353,15 +2353,20 @@ def test_main_use_union_operator(output_dir: Path) -> None: ) -@pytest.mark.parametrize("as_module", [True, False]) -def test_treat_dot_as_module(as_module: bool, output_dir: Path) -> None: +@pytest.mark.parametrize( + ("extra_args", "expected_suffix"), + [ + (["--treat-dot-as-module"], "treat_dot_as_module"), + (None, "treat_dot_not_as_module"), + (["--no-treat-dot-as-module"], "treat_dot_not_as_module"), + ], +) +def test_treat_dot_as_module(extra_args: list[str] | None, expected_suffix: str, output_dir: Path) -> None: """Test dot notation as module separator.""" - path_extension = "treat_dot_as_module" if as_module else "treat_dot_not_as_module" - extra_args = ["--treat-dot-as-module"] if as_module else None run_main_and_assert( input_path=JSON_SCHEMA_DATA_PATH / "treat_dot_as_module", output_path=output_dir, - expected_directory=EXPECTED_JSON_SCHEMA_PATH / path_extension, + expected_directory=EXPECTED_JSON_SCHEMA_PATH / expected_suffix, extra_args=extra_args, ) @@ -2385,6 +2390,64 @@ def test_treat_dot_as_module_single_file(output_dir: Path) -> None: ) +@pytest.mark.cli_doc( + options=["--no-treat-dot-as-module"], + input_schema="jsonschema/treat_dot_as_module_single", + cli_args=["--no-treat-dot-as-module"], + golden_output="jsonschema/treat_dot_as_module_single_no_treat/", + primary=True, +) +def test_no_treat_dot_as_module_single_file(output_dir: Path) -> None: + """Keep dots in schema names as underscores for flat output. + + The `--no-treat-dot-as-module` flag prevents splitting dotted schema names. + """ + run_main_and_assert( + input_path=JSON_SCHEMA_DATA_PATH / "treat_dot_as_module_single", + output_path=output_dir, + expected_directory=EXPECTED_JSON_SCHEMA_PATH / "treat_dot_as_module_single_no_treat", + extra_args=["--no-treat-dot-as-module"], + ) + + +@pytest.mark.parametrize( + ("extra_args", "expected_suffix"), + [ + (["--treat-dot-as-module"], "treat_dot_single"), + (None, "no_treat_dot_single"), + (["--no-treat-dot-as-module"], "no_treat_dot_single"), + ], +) +def test_treat_dot_as_module_version_style( + extra_args: list[str] | None, expected_suffix: str, output_dir: Path +) -> None: + """Test dotted version-style schema names (e.g., v0.0.39.job.json).""" + run_main_and_assert( + input_path=JSON_SCHEMA_DATA_PATH / "no_treat_dot_single", + output_path=output_dir, + expected_directory=EXPECTED_JSON_SCHEMA_PATH / expected_suffix, + extra_args=extra_args, + ) + + +@pytest.mark.parametrize( + ("extra_args", "expected_suffix"), + [ + (["--treat-dot-as-module"], "treat_dot_complex_treat"), + (None, "treat_dot_complex_no_treat"), + (["--no-treat-dot-as-module"], "treat_dot_complex_no_treat"), + ], +) +def test_treat_dot_as_module_complex_refs(extra_args: list[str] | None, expected_suffix: str, output_dir: Path) -> None: + """Test dotted schema names with cross-file references.""" + run_main_and_assert( + input_path=JSON_SCHEMA_DATA_PATH / "treat_dot_complex", + output_path=output_dir, + expected_directory=EXPECTED_JSON_SCHEMA_PATH / expected_suffix, + extra_args=extra_args, + ) + + def test_main_jsonschema_duplicate_name(output_dir: Path) -> None: """Test duplicate name handling.""" run_main_and_assert( diff --git a/tests/model/test_base.py b/tests/model/test_base.py index f6e0fa4c2..29e09fa89 100644 --- a/tests/model/test_base.py +++ b/tests/model/test_base.py @@ -303,7 +303,7 @@ def test_sanitize_module_name(name: str, expected_true: str, expected_false: str ("treat_dot_as_module", "expected"), [ (True, ["inputs", "array_commons.schema", "array-commons"]), - (False, ["inputs", "array_commons_schema", "array-commons"]), + (False, ["inputs", "array_commons_schema"]), ], ) def test_get_module_path_with_file_path(treat_dot_as_module: bool, expected: list[str]) -> None: @@ -313,23 +313,29 @@ def test_get_module_path_with_file_path(treat_dot_as_module: bool, expected: lis assert result == expected -@pytest.mark.parametrize("treat_dot_as_module", [True, False]) -def test_get_module_path_without_file_path(treat_dot_as_module: bool) -> None: - """Test module path generation without a file path.""" - result = get_module_path("my_module.submodule", None, treat_dot_as_module=treat_dot_as_module) +def test_get_module_path_without_file_path_treat_dot_true() -> None: + """Test module path generation without a file path with treat_dot_as_module=True.""" + result = get_module_path("my_module.submodule", None, treat_dot_as_module=True) expected = ["my_module"] assert result == expected +def test_get_module_path_without_file_path_treat_dot_false() -> None: + """Test module path generation without a file path with treat_dot_as_module=False.""" + result = get_module_path("my_module.submodule", None, treat_dot_as_module=False) + expected: list[str] = [] + assert result == expected + + @pytest.mark.parametrize( ("treat_dot_as_module", "name", "expected"), [ (True, "a.b.c", ["a", "b"]), (True, "simple", []), (True, "with.dot", ["with"]), - (False, "a.b.c", ["a", "b"]), + (False, "a.b.c", []), (False, "simple", []), - (False, "with.dot", ["with"]), + (False, "with.dot", []), ], ) def test_get_module_path_without_file_path_parametrized(