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

Regex pattern in JSON schema results in PyLance error

Open janeadams-ozette opened this issue 1 year ago • 2 comments

I believe the pattern matching described in #470 may no longer be compatible with PyLance. For example, creating a json schema like:

properties = {
    'session': {
        'type': 'string',
        'pattern': '^((staging)|(production)):{0-9}+',

results in a PyDantic model:

class State(BaseModel):
    session: Optional[constr(regex=r'^((staging)|(production)):{0-9}+')]

but this triggers

Call expression not allowed in type expression Pylance[reportInvalidTypeForm](https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportInvalidTypeForm).

See this issue via PyLance for more detail.

janeadams-ozette avatar May 27 '24 19:05 janeadams-ozette

this happens with integer type when min/max is applied as well.

sample schema:

{
	"$schema": "http://json-schema.org/draft-07/schema#",
	"type": "object",
	"properties": {
		"adapter_port": {
			"type": "integer",
			"description": "The adapter's SHDR port",
			"default": 7878,
			"minimum": 1,
			"maximum": 65535
		},
		"agent_port": {
			"type": "integer",
			"description": "MTConnect Agent port",
			"default": 5000,
			"minimum": 1,
			"maximum": 65535
		}
	},
	"required": [
		"adapter_port",
		"agent_port"
	]
}

output:

# generated by datamodel-codegen:
#   filename:  schema.json
#   timestamp: 2024-10-14T22:59:46+00:00

from __future__ import annotations

from pydantic import BaseModel, Field, conint


class Model(BaseModel):
    adapter_port: conint(ge=1, le=65535) = Field(
        ..., description="The adapter's SHDR port"
    )
    agent_port: conint(ge=1, le=65535) = Field(..., description='MTConnect Agent port')

and pylance complains: image

Edit to add the docs vscode shows me:

!!! warning "Discouraged"

This function is discouraged in favor of using Annotated with [Field][pydantic.fields.Field] instead. This function will be deprecated in Pydantic 3.0. The reason is that conint returns a type, which doesn't play well with static analysis tools. === ":x: Don't do this"

py
        from pydantic import BaseModel, conint

        class Foo(BaseModel):
            bar: conint(strict=True, gt=0)


=== ":white_check_mark: Do this"
py
        from typing_extensions import Annotated

        from pydantic import BaseModel, Field

        class Foo(BaseModel):
            bar: Annotated[int, Field(strict=True, gt=0)]

A wrapper around int that allows for additional constraints.

Args strict Whether to validate the integer in strict mode. Defaults to None.

gt The value must be greater than this.

ge The value must be greater than or equal to this.

lt The value must be less than this.

le The value must be less than or equal to this.

multiple_of The value must be a multiple of this.

Returns out The wrapped integer type.

py
from pydantic import BaseModel, ValidationError, conint

class ConstrainedExample(BaseModel):
    constrained_int: conint(gt=1)

m = ConstrainedExample(constrained_int=2)
print(repr(m))
#> ConstrainedExample(constrained_int=2)

try:
    ConstrainedExample(constrained_int=0)
except ValidationError as e:
    print(e.errors())
    '''
    [
        {
            'type': 'greater_than',
            'loc': ('constrained_int',),
            'msg': 'Input should be greater than 1',
            'input': 0,
            'ctx': {'gt': 1},
            'url': 'https://errors.pydantic.dev/2/v/greater_than',
        }
    ]
    '''

robot-ranger avatar Oct 14 '24 23:10 robot-ranger

I was able to overcome this issue by passing --use-annotated and --field-constraints , with the mention that the docs for --use-annotated lead you to believe that field constraints is enabled by default. Maybe that's the case when using the CLI, but I was using the module programmatically

mistrate avatar May 18 '25 19:05 mistrate