<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->

## Change Summary

<!-- Please give a short summary of the changes. -->

This fixes an issue where the JSON schema can end up using the drive letter (like `C`, `D` etc) instead of the model name on Windows if the module with the model is imported from an absolute path on the filesystem (for example using importlib).  This was caused by splitting at the first colon `:` character to remove it and the ID following it, thus on an absolute path on Windows of the form `C:\...` ended up becoming just the drive letter. This fix changes it to remove the LAST colon character and anything following it.

Modified description post-further investigation of the issue / source of the problem:

This makes the model name used in the JSON Schema be correct also if the model is loaded from a module that has a colon in the name. This should not really happen in any correct use case (python modules should not typically have colons in them), but it's possible to achieve by accident if you use the absolute file path as the module name on Windows when working with importlib. In that case you would have ended up getting just the drive letter as a model name in the JSON Schema, this fix ensures it will still work correctly in that case.



## Related issue number

<!-- WARNING: please use "fix #123" style references so the issue is closed when this PR is merged. -->

fix #7860

## Checklist

* [x] The pull request title is a good summary of the changes - it will be used in the changelog
* [ ] Unit tests for the changes exist
* [x] Tests pass on CI
* [ ] Documentation reflects the changes where applicable
* [ ] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**

Make the model name generation more robust in JSON schema. <!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->

## Change Summary

<!-- Please give a short summary of the changes. -->

This fixes an issue where the JSON schema can end up using the drive letter (like `C`, `D` etc) instead of the model name on Windows if the module with the model is imported from an absolute path on the filesystem (for example using importlib).  This was caused by splitting at the first colon `:` character to remove it and the ID following it, thus on an absolute path on Windows of the form `C:\...` ended up becoming just the drive letter. This fix changes it to remove the LAST colon character and anything following it.

Modified description post-further investigation of the issue / source of the problem:

This makes the model name used in the JSON Schema be correct also if the model is loaded from a module that has a colon in the name. This should not really happen in any correct use case (python modules should not typically have colons in them), but it's possible to achieve by accident if you use the absolute file path as the module name on Windows when working with importlib. In that case you would have ended up getting just the drive letter as a model name in the JSON Schema, this fix ensures it will still work correctly in that case.



## Related issue number

<!-- WARNING: please use "fix #123" style references so the issue is closed when this PR is merged. -->

fix #7860

## Checklist

* [x] The pull request title is a good summary of the changes - it will be used in the changelog
* [ ] Unit tests for the changes exist
* [x] Tests pass on CI
* [ ] Documentation reflects the changes where applicable
* [ ] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**

# Performance tips

In most cases Pydantic won't be your bottle neck, only follow this if you're sure it's necessary.

## Use `model_validate_json()` not `model_validate(json.loads(...))`

On `model_validate(json.loads(...))`, the JSON is parsed in Python, then converted to a dict, then it's validated internally.
On the other hand, `model_validate_json()` already performs the validation internally.

## `TypeAdapter` instantiated once

The idea here is to avoid constructing validators and serializers more than necessary. Each time a `TypeAdapter` is instantiated,
it will construct a new validator and serializer. If you're using a `TypeAdapter` in a function, it will be instantiated each time
the function is called. Instead, instantiate it once, and reuse it.

=== ":x: Bad"

    ```py lint="skip"
    from typing import List

    from pydantic import TypeAdapter


    def my_func():
        adapter = TypeAdapter(List[int])
        # do something with adapter
    ```

=== ":white_check_mark: Good"

    ```py lint="skip"
    from typing import List

    from pydantic import TypeAdapter

    adapter = TypeAdapter(List[int])

    def my_func():
        ...
        # do something with adapter
    ```

## `Sequence` vs `list` or `tuple` - `Mapping` vs `dict`

When using `Sequence`, Pydantic calls `isinstance(value, Sequence)` to check if the value is a sequence.
Also, Pydantic will try to validate against different types of sequences, like `list` and `tuple`.
If you know the value is a `list` or `tuple`, use `list` or `tuple` instead of `Sequence`.

The same applies to `Mapping` and `dict`.
If you know the value is a `dict`, use `dict` instead of `Mapping`.

## Don't do validation when you don't have to - use `Any` to keep the value unchanged

If you don't need to validate a value, use `Any` to keep the value unchanged.

```py
from typing import Any

from pydantic import BaseModel


class Model(BaseModel):
    a: Any


model = Model(a=1)
```

## Avoid extra information via subclasses of primitives

=== "Don't do this"

    ```py
    class CompletedStr(str):
        def __init__(self, s: str):
            self.s = s
            self.done = False
    ```

=== "Do this"

    ```py
    from pydantic import BaseModel


    class CompletedModel(BaseModel):
        s: str
        done: bool = False
    ```

===

## Use tagged union, not union

Tagged union (or discriminated union) is a union with a field that indicates which type it is.

```py test="skip"
from typing import Any

from typing_extensions import Literal

from pydantic import BaseModel, Field


class DivModel(BaseModel):
    el_type: Literal['div'] = 'div'
    class_name: str | None = None
    children: list[Any] | None = None


class SpanModel(BaseModel):
    el_type: Literal['span'] = 'span'
    class_name: str | None = None
    contents: str | None = None


class ButtonModel(BaseModel):
    el_type: Literal['button'] = 'button'
    class_name: str | None = None
    contents: str | None = None


class InputModel(BaseModel):
    el_type: Literal['input'] = 'input'
    class_name: str | None = None
    value: str | None = None


class Html(BaseModel):
    contents: DivModel | SpanModel | ButtonModel | InputModel = Field(
        discriminator='el_type'
    )
```

## Use `Literal` not `Enum`

Instead of using `Enum`, use `Literal` to define the structure of the data.

??? info "Performance comparison"
    With a simple benchmark, `Literal` is about ~3x faster than `Enum`:

    ```py test="skip"
    import enum
    from timeit import timeit

    from typing_extensions import Literal

    from pydantic import TypeAdapter

    ta = TypeAdapter(Literal['a', 'b'])
    result1 = timeit(lambda: ta.validate_python('a'), number=10000)


    class AB(str, enum.Enum):
        a = 'a'
        b = 'b'


    ta = TypeAdapter(AB)
    result2 = timeit(lambda: ta.validate_python('a'), number=10000)
    print(result2 / result1)
    ```

## Use `TypedDict` over nested models

Instead of using nested models, use `TypedDict` to define the structure of the data.

??? info "Performance comparison"
    With a simple benchmark, `TypedDict` is about ~2.5x faster than nested models:

    ```py test="skip"
    from timeit import timeit

    from typing_extensions import TypedDict

    from pydantic import BaseModel, TypeAdapter


    class A(TypedDict):
        a: str
        b: int


    class TypedModel(TypedDict):
        a: A


    class B(BaseModel):
        a: str
        b: int


    class Model(BaseModel):
        b: B


    ta = TypeAdapter(TypedModel)
    result1 = timeit(
        lambda: ta.validate_python({'a': {'a': 'a', 'b': 2}}), number=10000
    )
    result2 = timeit(
        lambda: Model.model_validate({'b': {'a': 'a', 'b': 2}}), number=10000
    )
    print(result2 / result1)
    ```

## Avoid wrap validators if you really care about performance

<!-- TODO: I need help on this one. -->

# Performance tips

In most cases Pydantic won't be your bottle neck, only follow this if you're sure it's necessary.

## Use `model_validate_json()` not `model_validate(json.loads(...))`

On `model_validate(json.loads(...))`, the JSON is parsed in Python, then converted to a dict, then it's validated internally.
On the other hand, `model_validate_json()` already performs the validation internally.

## `TypeAdapter` instantiated once

The idea here is to avoid constructing validators and serializers more than necessary. Each time a `TypeAdapter` is instantiated,
it will construct a new validator and serializer. If you're using a `TypeAdapter` in a function, it will be instantiated each time
the function is called. Instead, instantiate it once, and reuse it.

=== ":x: Bad"

    ```py lint="skip"
    from typing import List

    from pydantic import TypeAdapter


    def my_func():
        adapter = TypeAdapter(List[int])
        # do something with adapter
    ```

=== ":white_check_mark: Good"

    ```py lint="skip"
    from typing import List

    from pydantic import TypeAdapter

    adapter = TypeAdapter(List[int])

    def my_func():
        ...
        # do something with adapter
    ```

## `Sequence` vs `list` or `tuple` - `Mapping` vs `dict`

When using `Sequence`, Pydantic calls `isinstance(value, Sequence)` to check if the value is a sequence.
Also, Pydantic will try to validate against different types of sequences, like `list` and `tuple`.
If you know the value is a `list` or `tuple`, use `list` or `tuple` instead of `Sequence`.

The same applies to `Mapping` and `dict`.
If you know the value is a `dict`, use `dict` instead of `Mapping`.

## Don't do validation when you don't have to - use `Any` to keep the value unchanged

If you don't need to validate a value, use `Any` to keep the value unchanged.

```py
from typing import Any

from pydantic import BaseModel


class Model(BaseModel):
    a: Any


model = Model(a=1)
```

## Avoid extra information via subclasses of primitives

=== "Don't do this"

    ```py
    class CompletedStr(str):
        def __init__(self, s: str):
            self.s = s
            self.done = False
    ```

=== "Do this"

    ```py
    from pydantic import BaseModel


    class CompletedModel(BaseModel):
        s: str
        done: bool = False
    ```

===

## Use tagged union, not union

Tagged union (or discriminated union) is a union with a field that indicates which type it is.

```py test="skip"
from typing import Any

from typing_extensions import Literal

from pydantic import BaseModel, Field


class DivModel(BaseModel):
    el_type: Literal['div'] = 'div'
    class_name: str | None = None
    children: list[Any] | None = None


class SpanModel(BaseModel):
    el_type: Literal['span'] = 'span'
    class_name: str | None = None
    contents: str | None = None


class ButtonModel(BaseModel):
    el_type: Literal['button'] = 'button'
    class_name: str | None = None
    contents: str | None = None


class InputModel(BaseModel):
    el_type: Literal['input'] = 'input'
    class_name: str | None = None
    value: str | None = None


class Html(BaseModel):
    contents: DivModel | SpanModel | ButtonModel | InputModel = Field(
        discriminator='el_type'
    )
```

## Use `Literal` not `Enum`

Instead of using `Enum`, use `Literal` to define the structure of the data.

??? info "Performance comparison"
    With a simple benchmark, `Literal` is about ~3x faster than `Enum`:

    ```py test="skip"
    import enum
    from timeit import timeit

    from typing_extensions import Literal

    from pydantic import TypeAdapter

    ta = TypeAdapter(Literal['a', 'b'])
    result1 = timeit(lambda: ta.validate_python('a'), number=10000)


    class AB(str, enum.Enum):
        a = 'a'
        b = 'b'


    ta = TypeAdapter(AB)
    result2 = timeit(lambda: ta.validate_python('a'), number=10000)
    print(result2 / result1)
    ```

## Use `TypedDict` over nested models

Instead of using nested models, use `TypedDict` to define the structure of the data.

??? info "Performance comparison"
    With a simple benchmark, `TypedDict` is about ~2.5x faster than nested models:

    ```py test="skip"
    from timeit import timeit

    from typing_extensions import TypedDict

    from pydantic import BaseModel, TypeAdapter


    class A(TypedDict):
        a: str
        b: int


    class TypedModel(TypedDict):
        a: A


    class B(BaseModel):
        a: str
        b: int


    class Model(BaseModel):
        b: B


    ta = TypeAdapter(TypedModel)
    result1 = timeit(
        lambda: ta.validate_python({'a': {'a': 'a', 'b': 2}}), number=10000
    )
    result2 = timeit(
        lambda: Model.model_validate({'b': {'a': 'a', 'b': 2}}), number=10000
    )
    print(result2 / result1)
    ```

## Avoid wrap validators if you really care about performance

<!-- TODO: I need help on this one. -->
