Skip to content

Commit 930fa74

Browse files
authored
Add --use-all-exports option to generate __all__ in __init__.py (#2586)
* Add --use-all-exports option to generate __all__ declaration in __init__.py * Refactor __init__.py and base.py to implement --use-all-exports option for dynamic __all__ declaration generation * Refactor base.py to streamline __all__ declaration generation for __init__.py with --use-all-exports option
1 parent e7afe6c commit 930fa74

28 files changed

Lines changed: 575 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,8 @@ Model customization:
490490
target python version
491491
--treat-dot-as-module
492492
treat dotted module names as modules
493+
--use-all-exports Generate __all__ = [...] in __init__.py to export all defined models
494+
and types
493495
--use-exact-imports import exact types instead of modules, for example: "from .foo
494496
import Bar" instead of "from . import foo" with "foo.Bar"
495497
--use-pendulum use pendulum instead of datetime

docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ Model customization:
482482
target python version
483483
--treat-dot-as-module
484484
treat dotted module names as modules
485+
--use-all-exports Generate __all__ = [...] in __init__.py to export all defined models
486+
and types
485487
--use-exact-imports import exact types instead of modules, for example: "from .foo
486488
import Bar" instead of "from . import foo" with "foo.Bar"
487489
--use-pendulum use pendulum instead of datetime

src/datamodel_code_generator/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
375375
dataclass_arguments: DataclassArguments | None = None,
376376
disable_future_imports: bool = False,
377377
type_mappings: list[str] | None = None,
378+
use_all_exports: bool = False,
378379
) -> None:
379380
"""Generate Python data models from schema definitions or structured data.
380381
@@ -612,7 +613,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
612613
)
613614

614615
with chdir(output):
615-
results = parser.parse(disable_future_imports=disable_future_imports)
616+
results = parser.parse(disable_future_imports=disable_future_imports, use_all_exports=use_all_exports)
616617
if not input_filename: # pragma: no cover
617618
if isinstance(input_, str):
618619
input_filename = "<stdin>"
@@ -628,7 +629,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
628629
msg = "Models not found in the input data"
629630
raise Error(msg)
630631
if isinstance(results, str):
631-
modules = {output: (results, input_filename)}
632+
modules: dict[Path | None, tuple[str, str | None]] = {output: (results, input_filename)}
632633
else:
633634
if output is None:
634635
msg = "Modular references require an output directory"

src/datamodel_code_generator/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ def validate_root(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
425425
parent_scoped_naming: bool = False
426426
disable_future_imports: bool = False
427427
type_mappings: Optional[list[str]] = None # noqa: UP045
428+
use_all_exports: bool = False
428429

429430
def merge_args(self, args: Namespace) -> None:
430431
"""Merge command-line arguments into config."""
@@ -826,6 +827,7 @@ def main(args: Sequence[str] | None = None) -> Exit: # noqa: PLR0911, PLR0912,
826827
dataclass_arguments=config.dataclass_arguments,
827828
disable_future_imports=config.disable_future_imports,
828829
type_mappings=config.type_mappings,
830+
use_all_exports=config.use_all_exports,
829831
)
830832
except InvalidClassNameError as e:
831833
print(f"{e} You have to set `--class-name` option", file=sys.stderr) # noqa: T201

src/datamodel_code_generator/arguments.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,12 @@ def start_section(self, heading: str | None) -> None:
293293
action="store_true",
294294
default=None,
295295
)
296+
model_options.add_argument(
297+
"--use-all-exports",
298+
help="Generate __all__ = [...] in __init__.py to export all defined models and types",
299+
action="store_true",
300+
default=None,
301+
)
296302

297303
# ======================================================================================
298304
# Typing options for generated models

src/datamodel_code_generator/parser/base.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,7 @@ def parse( # noqa: PLR0912, PLR0914, PLR0915
15681568
format_: bool | None = True, # noqa: FBT001, FBT002
15691569
settings_path: Path | None = None,
15701570
disable_future_imports: bool = False, # noqa: FBT001, FBT002
1571+
use_all_exports: bool = False, # noqa: FBT001, FBT002
15711572
) -> str | dict[tuple[str, ...], Result]:
15721573
"""Parse schema and generate code, returning single file or module dict."""
15731574
self.parse_raw()
@@ -1709,10 +1710,20 @@ class Processed(NamedTuple):
17091710

17101711
for module, models, init, imports, scoped_model_resolver in processed_models: # noqa: B007
17111712
result: list[str] = []
1713+
export_names: list[str] = []
17121714
if models:
17131715
if with_import:
17141716
result += [str(self.imports), str(imports), "\n"]
17151717

1718+
if use_all_exports and module[-1] == "__init__.py":
1719+
export_names = [
1720+
m.reference.short_name
1721+
for m in models
1722+
if m.reference and not m.reference.short_name.startswith("_")
1723+
]
1724+
all_items = ",\n ".join(f'"{name}"' for name in export_names)
1725+
result += [f"__all__ = [\n {all_items},\n]\n"]
1726+
17161727
self.__update_type_aliases(models)
17171728
code = dump_templates(models)
17181729
result += [code]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Custom module docstring header.
3+
4+
This is a multi-line docstring used for testing.
5+
"""
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# generated by datamodel-codegen:
2+
# filename: modular.yaml
3+
4+
from __future__ import annotations
5+
6+
from typing import Optional
7+
8+
from pydantic import BaseModel
9+
10+
from . import foo as foo_1
11+
from . import models
12+
from .nested import foo as foo_2
13+
14+
__all__ = [
15+
"OptionalModel",
16+
"Id",
17+
"Error",
18+
"Result",
19+
"Source",
20+
"DifferentTea",
21+
]
22+
23+
24+
class OptionalModel(BaseModel):
25+
__root__: str
26+
27+
28+
class Id(BaseModel):
29+
__root__: str
30+
31+
32+
class Error(BaseModel):
33+
code: int
34+
message: str
35+
36+
37+
class Result(BaseModel):
38+
event: Optional[models.Event] = None
39+
40+
41+
class Source(BaseModel):
42+
country: Optional[str] = None
43+
44+
45+
class DifferentTea(BaseModel):
46+
foo: Optional[foo_1.Tea] = None
47+
nested: Optional[foo_2.Tea] = None
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# generated by datamodel-codegen:
2+
# filename: modular.yaml
3+
4+
from __future__ import annotations
5+
6+
from pydantic import BaseModel, Field
7+
8+
9+
class FieldModel(BaseModel):
10+
__root__: str = Field(..., example='green')
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# generated by datamodel-codegen:
2+
# filename: modular.yaml
3+
4+
from __future__ import annotations
5+
6+
from enum import Enum
7+
from typing import List, Optional
8+
9+
from pydantic import AnyUrl, BaseModel, Field
10+
11+
from . import models
12+
13+
14+
class Pets(BaseModel):
15+
__root__: List[models.Pet]
16+
17+
18+
class Users(BaseModel):
19+
__root__: List[models.User]
20+
21+
22+
class Rules(BaseModel):
23+
__root__: List[str]
24+
25+
26+
class Stage(Enum):
27+
test = 'test'
28+
dev = 'dev'
29+
stg = 'stg'
30+
prod = 'prod'
31+
32+
33+
class Api(BaseModel):
34+
apiKey: Optional[str] = Field(
35+
None, description='To be used as a dataset parameter value'
36+
)
37+
apiVersionNumber: Optional[str] = Field(
38+
None, description='To be used as a version parameter value'
39+
)
40+
apiUrl: Optional[AnyUrl] = Field(
41+
None, description="The URL describing the dataset's fields"
42+
)
43+
apiDocumentationUrl: Optional[AnyUrl] = Field(
44+
None, description='A URL to the API console for each API'
45+
)
46+
stage: Optional[Stage] = None
47+
48+
49+
class Apis(BaseModel):
50+
__root__: List[Api]

0 commit comments

Comments
 (0)