Files
Denis Artyushin a36ce94f2d Add custom formatters (#1733)
* Support custom formatters for CodeFormatter

* Add custom formatters argument

* Add graphql to docs/supported-data-types.md

* Add test

custom formatter for custom-scalar-types.graphql;

* Run poetry run scripts/format.sh

* Add simple doc
2023-11-25 00:59:32 +09:00

476 lines
14 KiB
Python

from __future__ import annotations
import locale
from argparse import ArgumentParser, FileType, HelpFormatter, Namespace
from operator import attrgetter
from typing import TYPE_CHECKING
from datamodel_code_generator import DataModelType, InputFileType, OpenAPIScope
from datamodel_code_generator.format import PythonVersion
from datamodel_code_generator.parser import LiteralType
from datamodel_code_generator.types import StrictTypes
if TYPE_CHECKING:
from argparse import Action
from typing import Iterable, Optional
DEFAULT_ENCODING = locale.getpreferredencoding()
namespace = Namespace(no_color=False)
class SortingHelpFormatter(HelpFormatter):
def _bold_cyan(self, text: str) -> str:
return f'\x1b[36;1m{text}\x1b[0m'
def add_arguments(self, actions: Iterable[Action]) -> None:
actions = sorted(actions, key=attrgetter('option_strings'))
super().add_arguments(actions)
def start_section(self, heading: Optional[str]) -> None:
return super().start_section(
heading if namespace.no_color or not heading else self._bold_cyan(heading)
)
arg_parser = ArgumentParser(
usage='\n datamodel-codegen [options]',
description='Generate Python data models from schema definitions or structured data',
formatter_class=SortingHelpFormatter,
add_help=False,
)
base_options = arg_parser.add_argument_group('Options')
typing_options = arg_parser.add_argument_group('Typing customization')
field_options = arg_parser.add_argument_group('Field customization')
model_options = arg_parser.add_argument_group('Model customization')
template_options = arg_parser.add_argument_group('Template customization')
openapi_options = arg_parser.add_argument_group('OpenAPI-only options')
general_options = arg_parser.add_argument_group('General options')
# ======================================================================================
# Base options for input/output
# ======================================================================================
base_options.add_argument(
'--http-headers',
nargs='+',
metavar='HTTP_HEADER',
help='Set headers in HTTP requests to the remote host. (example: "Authorization: Basic dXNlcjpwYXNz")',
)
base_options.add_argument(
'--http-ignore-tls',
help="Disable verification of the remote host's TLS certificate",
action='store_true',
default=None,
)
base_options.add_argument(
'--input',
help='Input file/directory (default: stdin)',
)
base_options.add_argument(
'--input-file-type',
help='Input file type (default: auto)',
choices=[i.value for i in InputFileType],
)
base_options.add_argument(
'--output',
help='Output file (default: stdout)',
)
base_options.add_argument(
'--output-model-type',
help='Output model type (default: pydantic.BaseModel)',
choices=[i.value for i in DataModelType],
)
base_options.add_argument(
'--url',
help='Input file URL. `--input` is ignored when `--url` is used',
)
# ======================================================================================
# Customization options for generated models
# ======================================================================================
model_options.add_argument(
'--allow-extra-fields',
help='Allow to pass extra fields, if this flag is not passed, extra fields are forbidden.',
action='store_true',
default=None,
)
model_options.add_argument(
'--allow-population-by-field-name',
help='Allow population by field name',
action='store_true',
default=None,
)
model_options.add_argument(
'--class-name',
help='Set class name of root model',
default=None,
)
model_options.add_argument(
'--collapse-root-models',
action='store_true',
default=None,
help='Models generated with a root-type field will be merged'
'into the models using that root-type model',
)
model_options.add_argument(
'--disable-appending-item-suffix',
help='Disable appending `Item` suffix to model name in an array',
action='store_true',
default=None,
)
model_options.add_argument(
'--disable-timestamp',
help='Disable timestamp on file headers',
action='store_true',
default=None,
)
model_options.add_argument(
'--enable-faux-immutability',
help='Enable faux immutability',
action='store_true',
default=None,
)
model_options.add_argument(
'--enable-version-header',
help='Enable package version on file headers',
action='store_true',
default=None,
)
model_options.add_argument(
'--keep-model-order',
help="Keep generated models' order",
action='store_true',
default=None,
)
model_options.add_argument(
'--reuse-model',
help='Re-use models on the field when a module has the model with the same content',
action='store_true',
default=None,
)
model_options.add_argument(
'--target-python-version',
help='target python version (default: 3.7)',
choices=[v.value for v in PythonVersion],
)
model_options.add_argument(
'--use-schema-description',
help='Use schema description to populate class docstring',
action='store_true',
default=None,
)
model_options.add_argument(
'--use-title-as-name',
help='use titles as class names of models',
action='store_true',
default=None,
)
# ======================================================================================
# Typing options for generated models
# ======================================================================================
typing_options.add_argument(
'--base-class',
help='Base Class (default: pydantic.BaseModel)',
type=str,
)
typing_options.add_argument(
'--enum-field-as-literal',
help='Parse enum field as literal. '
'all: all enum field type are Literal. '
'one: field type is Literal when an enum has only one possible value',
choices=[lt.value for lt in LiteralType],
default=None,
)
typing_options.add_argument(
'--field-constraints',
help='Use field constraints and not con* annotations',
action='store_true',
default=None,
)
typing_options.add_argument(
'--set-default-enum-member',
help='Set enum members as default values for enum field',
action='store_true',
default=None,
)
typing_options.add_argument(
'--strict-types',
help='Use strict types',
choices=[t.value for t in StrictTypes],
nargs='+',
)
typing_options.add_argument(
'--use-annotated',
help='Use typing.Annotated for Field(). Also, `--field-constraints` option will be enabled.',
action='store_true',
default=None,
)
typing_options.add_argument(
'--use-generic-container-types',
help='Use generic container types for type hinting (typing.Sequence, typing.Mapping). '
'If `--use-standard-collections` option is set, then import from collections.abc instead of typing',
action='store_true',
default=None,
)
typing_options.add_argument(
'--use-non-positive-negative-number-constrained-types',
help='Use the Non{Positive,Negative}{FloatInt} types instead of the corresponding con* constrained types.',
action='store_true',
default=None,
)
typing_options.add_argument(
'--use-one-literal-as-default',
help='Use one literal as default value for one literal field',
action='store_true',
default=None,
)
typing_options.add_argument(
'--use-standard-collections',
help='Use standard collections for type hinting (list, dict)',
action='store_true',
default=None,
)
typing_options.add_argument(
'--use-subclass-enum',
help='Define Enum class as subclass with field type when enum has type (int, float, bytes, str)',
action='store_true',
default=None,
)
typing_options.add_argument(
'--use-union-operator',
help='Use | operator for Union type (PEP 604).',
action='store_true',
default=None,
)
typing_options.add_argument(
'--use-unique-items-as-set',
help='define field type as `set` when the field attribute has `uniqueItems`',
action='store_true',
default=None,
)
# ======================================================================================
# Customization options for generated model fields
# ======================================================================================
field_options.add_argument(
'--capitalise-enum-members',
'--capitalize-enum-members',
help='Capitalize field names on enum',
action='store_true',
default=None,
)
field_options.add_argument(
'--empty-enum-field-name',
help='Set field name when enum value is empty (default: `_`)',
default=None,
)
field_options.add_argument(
'--field-extra-keys',
help='Add extra keys to field parameters',
type=str,
nargs='+',
)
field_options.add_argument(
'--field-extra-keys-without-x-prefix',
help='Add extra keys with `x-` prefix to field parameters. The extra keys are stripped of the `x-` prefix.',
type=str,
nargs='+',
)
field_options.add_argument(
'--field-include-all-keys',
help='Add all keys to field parameters',
action='store_true',
default=None,
)
field_options.add_argument(
'--force-optional',
help='Force optional for required fields',
action='store_true',
default=None,
)
field_options.add_argument(
'--original-field-name-delimiter',
help='Set delimiter to convert to snake case. This option only can be used with --snake-case-field (default: `_` )',
default=None,
)
field_options.add_argument(
'--remove-special-field-name-prefix',
help='Remove field name prefix if it has a special meaning e.g. underscores',
action='store_true',
default=None,
)
field_options.add_argument(
'--snake-case-field',
help='Change camel-case field name to snake-case',
action='store_true',
default=None,
)
field_options.add_argument(
'--special-field-name-prefix',
help="Set field name prefix when first character can't be used as Python field name (default: `field`)",
default=None,
)
field_options.add_argument(
'--strip-default-none',
help='Strip default None on fields',
action='store_true',
default=None,
)
field_options.add_argument(
'--use-default',
help='Use default value even if a field is required',
action='store_true',
default=None,
)
field_options.add_argument(
'--use-default-kwarg',
action='store_true',
help='Use `default=` instead of a positional argument for Fields that have default values.',
default=None,
)
field_options.add_argument(
'--use-field-description',
help='Use schema description to populate field docstring',
action='store_true',
default=None,
)
# ======================================================================================
# Options for templating output
# ======================================================================================
template_options.add_argument(
'--aliases',
help='Alias mapping file',
type=FileType('rt'),
)
template_options.add_argument(
'--custom-file-header',
help='Custom file header',
type=str,
default=None,
)
template_options.add_argument(
'--custom-file-header-path',
help='Custom file header file path',
default=None,
type=str,
)
template_options.add_argument(
'--custom-template-dir',
help='Custom template directory',
type=str,
)
template_options.add_argument(
'--encoding',
help=f'The encoding of input and output (default: {DEFAULT_ENCODING})',
default=None,
)
template_options.add_argument(
'--extra-template-data',
help='Extra template data',
type=FileType('rt'),
)
template_options.add_argument(
'--use-double-quotes',
action='store_true',
default=None,
help='Model generated with double quotes. Single quotes or '
'your black config skip_string_normalization value will be used without this option.',
)
template_options.add_argument(
'--wrap-string-literal',
help='Wrap string literal by using black `experimental-string-processing` option (require black 20.8b0 or later)',
action='store_true',
default=None,
)
base_options.add_argument(
'--additional-imports',
help='Custom imports for output (delimited list input). For example "datetime.date,datetime.datetime"',
type=str,
default=None,
)
base_options.add_argument(
'--custom-formatters',
help='List of modules with custom formatter (delimited list input).',
type=str,
default=None,
)
template_options.add_argument(
'--custom-formatters-kwargs',
help='A file with kwargs for custom formatters.',
type=FileType('rt'),
)
# ======================================================================================
# Options specific to OpenAPI input schemas
# ======================================================================================
openapi_options.add_argument(
'--openapi-scopes',
help='Scopes of OpenAPI model generation (default: schemas)',
choices=[o.value for o in OpenAPIScope],
nargs='+',
default=None,
)
openapi_options.add_argument(
'--strict-nullable',
help='Treat default field as a non-nullable field (Only OpenAPI)',
action='store_true',
default=None,
)
openapi_options.add_argument(
'--use-operation-id-as-name',
help='use operation id of OpenAPI as class names of models',
action='store_true',
default=None,
)
openapi_options.add_argument(
'--validation',
help='Deprecated: Enable validation (Only OpenAPI). this option is deprecated. it will be removed in future '
'releases',
action='store_true',
default=None,
)
# ======================================================================================
# General options
# ======================================================================================
general_options.add_argument(
'--debug',
help='show debug message (require "debug". `$ pip install \'datamodel-code-generator[debug]\'`)',
action='store_true',
default=None,
)
general_options.add_argument(
'--disable-warnings',
help='disable warnings',
action='store_true',
default=None,
)
general_options.add_argument(
'-h',
'--help',
action='help',
default='==SUPPRESS==',
help='show this help message and exit',
)
general_options.add_argument(
'--no-color',
action='store_true',
default=False,
help='disable colorized output',
)
general_options.add_argument(
'--version',
action='store_true',
help='show version',
)
__all__ = [
'arg_parser',
'DEFAULT_ENCODING',
'namespace',
]