sain.convert

Protocols for conversions between types.

The protocols in this module provide a way to convert from one type to another type. Each trait serves a different purpose:

  • Implement the From trait for consuming value-to-value conversions
  • Implement the Into trait for consuming value-to-value conversions to types outside the current crate
  • The TryFrom and TryInto traits behave like From and Into, but should be implemented when the conversion can fail.
  • Implement the ToString trait for explicitly converting objects to string.
Example
@dataclass
class Id(From[UUID], Into[int]):
    id: int | float

    @classmethod
    def from_value(cls, value: UUID) -> Self:
        # Keep in mind, this stores a 128 bit <long> integer.
        return cls(int(value))

    def into(self) -> int:
        return int(self.id)

# Simply perform conversions.
from_uuid = Id.from_value(uuid4())
into_int = from_uuid.into()

For type conversions that may fail, two safe interfaces, TryInto and TryFrom exist which deal with that.

This is useful when you are doing a type conversion that may trivially succeed but may also need special handling.

@dataclass
class Message(Into[bytes], TryFrom[bytes, None]):
    content: str
    id: int

    def into(self) -> bytes:
        return json.dumps(self.__dict__).encode()

    @classmethod
    def try_from(cls, value: bytes) -> Result[Self, None]:
        try:
            payload = json.loads(value)
            return Ok(cls(content=payload['content'], id=payload['id']))
        except (json.decoder.JSONDecodeError, KeyError):
            # Its rare to see a JSONDecodeError raised, but usually
            # keys goes missing, which raises a KeyError.
            return Err(None)

message_bytes = b'{"content": "content", "id": 0}'

match Message.try_from(message_bytes):
    case Ok(message):
        print("Successful conversion", message)
    case Err(invalid_bytes):
        print("Invalid bytes:", invalid_bytes)

payload = Message(content='content', id=0)
assert payload.into() == message_bytes
  1# BSD 3-Clause License
  2#
  3# Copyright (c) 2022-Present, nxtlo
  4# All rights reserved.
  5#
  6# Redistribution and use in source and binary forms, with or without
  7# modification, are permitted provided that the following conditions are met:
  8#
  9# * Redistributions of source code must retain the above copyright notice, this
 10#   list of conditions and the following disclaimer.
 11#
 12# * Redistributions in binary form must reproduce the above copyright notice,
 13#   this list of conditions and the following disclaimer in the documentation
 14#   and/or other materials provided with the distribution.
 15#
 16# * Neither the name of the copyright holder nor the names of its
 17#   contributors may be used to endorse or promote products derived from
 18#   this software without specific prior written permission.
 19#
 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30
 31"""
 32Protocols for conversions between types.
 33
 34The protocols in this module provide a way to convert from one type to another type. Each trait serves a different purpose:
 35
 36* Implement the From trait for consuming value-to-value conversions
 37* Implement the Into trait for consuming value-to-value conversions to types outside the current crate
 38* The TryFrom and TryInto traits behave like From and Into, but should be implemented when the conversion can fail.
 39* Implement the `ToString` trait for explicitly converting objects to string.
 40
 41Example
 42--------
 43```py
 44@dataclass
 45class Id(From[UUID], Into[int]):
 46    id: int | float
 47
 48    @classmethod
 49    def from_value(cls, value: UUID) -> Self:
 50        # Keep in mind, this stores a 128 bit <long> integer.
 51        return cls(int(value))
 52
 53    def into(self) -> int:
 54        return int(self.id)
 55
 56# Simply perform conversions.
 57from_uuid = Id.from_value(uuid4())
 58into_int = from_uuid.into()
 59```
 60
 61For type conversions that may fail, two safe interfaces, `TryInto` and `TryFrom` exist which deal with that.
 62
 63This is useful when you are doing a type conversion that may trivially succeed but may also need special handling.
 64
 65```py
 66@dataclass
 67class Message(Into[bytes], TryFrom[bytes, None]):
 68    content: str
 69    id: int
 70
 71    def into(self) -> bytes:
 72        return json.dumps(self.__dict__).encode()
 73
 74    @classmethod
 75    def try_from(cls, value: bytes) -> Result[Self, None]:
 76        try:
 77            payload = json.loads(value)
 78            return Ok(cls(content=payload['content'], id=payload['id']))
 79        except (json.decoder.JSONDecodeError, KeyError):
 80            # Its rare to see a JSONDecodeError raised, but usually
 81            # keys goes missing, which raises a KeyError.
 82            return Err(None)
 83
 84message_bytes = b'{"content": "content", "id": 0}'
 85
 86match Message.try_from(message_bytes):
 87    case Ok(message):
 88        print("Successful conversion", message)
 89    case Err(invalid_bytes):
 90        print("Invalid bytes:", invalid_bytes)
 91
 92payload = Message(content='content', id=0)
 93assert payload.into() == message_bytes
 94```
 95"""
 96
 97from __future__ import annotations
 98
 99__slots__ = ("Into", "TryInto", "From", "TryFrom", "ToString")
100
101import typing
102
103from sain.macros import rustc_diagnostic_item
104
105if typing.TYPE_CHECKING:
106    from typing_extensions import Self
107
108    from sain import Result
109
110T = typing.TypeVar("T")
111T_co = typing.TypeVar("T_co", contravariant=True)
112T_cov = typing.TypeVar("T_cov", covariant=True)
113E = typing.TypeVar("E")
114
115
116@rustc_diagnostic_item("convert_identity")
117def identity(x: T) -> T:
118    """The identity function.
119
120    This is a function that returns the same value as the input.
121
122    While it might seem strange to have a function that just returns back the input, there are some interesting uses.
123
124    Example
125    -------
126    Using `identity` to do nothing in a sequence of operations.
127    ```py
128    from sain.convert import identity
129
130    def mangle(x: int) -> int:
131        return x + 1
132
133    arr = [identity, mangle]
134    ```
135
136    Using `identity` to do nothing conditionally.
137    ```py
138    from sain.convert import identity
139
140    do_stuff = identity if condition else mangle
141    results = do_stuff(5)
142    ```
143    """
144    return x
145
146
147@rustc_diagnostic_item("From")
148@typing.runtime_checkable
149class From(typing.Protocol[T_co]):
150    """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
151
152    As the Rust documentation says, One should always prefer implementing From over Into
153    because implementing From automatically provides one with an implementation of Into.
154
155    But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types
156    that impl `From<T>`.
157
158    So for the sake of simplicity, You should implement whichever interface you want deal with,
159    Or simply, implement both as the same time.
160
161    Example
162    -------
163    ```py
164    @dataclass
165    class Id(From[str]):
166        value: int
167
168        @classmethod
169        def from_value(cls, value: str) -> Self:
170            return cls(value=int(value))
171
172    ```
173    """
174
175    __slots__ = ()
176
177    @classmethod
178    def from_value(cls, value: T_co) -> Self:
179        """Perform the conversion."""
180        raise NotImplementedError
181
182
183@rustc_diagnostic_item("TryFrom")
184@typing.runtime_checkable
185class TryFrom(typing.Protocol[T_co, E]):
186    """Simple and safe type conversions that may fail in a controlled way under some circumstances.
187    It is the reciprocal of `TryInto`.
188
189    It is useful to implement this when you know that the conversion may fail in some way.
190
191    Generic Implementations
192    -------------------
193    This interface takes two type arguments, and return `Result<Self, E>`
194
195    * `T`: Which's the first generic `T` is the type that's being converted from.
196    * `E`: If the conversion fails in a way, this is what will return as the error.
197    * `Self`: Which's the instance of the class that is being converted into.
198
199    Example
200    -------
201    ```py
202    @dataclass
203    class Id(TryFrom[str, str]):
204        value: int
205
206        @classmethod
207        def try_from(cls, value: str) -> Result[Self, str]:
208            if not value.isnumeric():
209                # NaN
210                return Err(f"Couldn't convert: {value} to self")
211            # otherwise convert it to an Id instance.
212            return Ok(value=cls(int(value)))
213    ```
214    """
215
216    __slots__ = ()
217
218    @classmethod
219    def try_from(cls, value: T_co) -> Result[Self, E]:
220        """Perform the conversion."""
221        raise NotImplementedError
222
223
224@rustc_diagnostic_item("TryFrom")
225@typing.runtime_checkable
226class Into(typing.Protocol[T_cov]):
227    """Conversion from `self`, which may or may not be expensive.
228
229    Example
230    -------
231    ```py
232    @dataclass
233    class Id(Into[str]):
234        value: int
235
236        def into(self) -> str:
237            return str(self.value)
238    ```
239    """
240
241    __slots__ = ()
242
243    def into(self) -> T_cov:
244        """Perform the conversion."""
245        raise NotImplementedError
246
247
248@rustc_diagnostic_item("TryInto")
249@typing.runtime_checkable
250class TryInto(typing.Protocol[T, E]):
251    """An attempted conversion from `self`, which may or may not be expensive.
252
253    It is useful to implement this when you know that the conversion may fail in some way.
254
255    Generic Implementations
256    -------------------
257    This interface takes two type arguments, and return `Result<T, E>`
258
259    * `T`: The first generic `T` is the type that's being converted into.
260    * `E`: If the conversion fails in a way, this is what will return as the error.
261
262    Example
263    -------
264    ```py
265    @dataclass
266    class Id(TryInto[int, str]):
267        value: str
268
269        def try_into(self) -> Result[int, str]:
270            if not self.value.isnumeric():
271                return Err(f"{self.value} is not a number...")
272            return Ok(int(self.value))
273    ```
274    """
275
276    __slots__ = ()
277
278    def try_into(self) -> Result[T, E]:
279        """Perform the conversion."""
280        raise NotImplementedError
281
282
283@rustc_diagnostic_item("ToString")
284@typing.runtime_checkable
285class ToString(typing.Protocol):
286    """A trait for explicitly converting a value to a `str`.
287
288    Example
289    -------
290    ```py
291    class Value[T: bytes](ToString):
292        buffer: T
293
294        def to_string(self) -> str:
295            return self.buffer.decode("utf-8")
296    ```
297    """
298
299    __slots__ = ()
300
301    def to_string(self) -> str:
302        """Converts the given value to a `str`.
303
304        Example
305        --------
306        ```py
307        i = 5  # assume `int` implements `ToString`
308        five = "5"
309        assert five == i.to_string()
310        ```
311        """
312        raise NotImplementedError
313
314    def __str__(self) -> str:
315        return self.to_string()
@rustc_diagnostic_item('convert_identity')
def identity(x: ~T) -> ~T:
117@rustc_diagnostic_item("convert_identity")
118def identity(x: T) -> T:
119    """The identity function.
120
121    This is a function that returns the same value as the input.
122
123    While it might seem strange to have a function that just returns back the input, there are some interesting uses.
124
125    Example
126    -------
127    Using `identity` to do nothing in a sequence of operations.
128    ```py
129    from sain.convert import identity
130
131    def mangle(x: int) -> int:
132        return x + 1
133
134    arr = [identity, mangle]
135    ```
136
137    Using `identity` to do nothing conditionally.
138    ```py
139    from sain.convert import identity
140
141    do_stuff = identity if condition else mangle
142    results = do_stuff(5)
143    ```
144    """
145    return x

The identity function.

This is a function that returns the same value as the input.

While it might seem strange to have a function that just returns back the input, there are some interesting uses.

Example

Using identity to do nothing in a sequence of operations.

from sain.convert import identity

def mangle(x: int) -> int:
    return x + 1

arr = [identity, mangle]

Using identity to do nothing conditionally.

from sain.convert import identity

do_stuff = identity if condition else mangle
results = do_stuff(5)

Implementations

This function implements convert_identity in Rust.

@rustc_diagnostic_item('From')
@typing.runtime_checkable
class From(typing.Protocol[-T_co]):
148@rustc_diagnostic_item("From")
149@typing.runtime_checkable
150class From(typing.Protocol[T_co]):
151    """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
152
153    As the Rust documentation says, One should always prefer implementing From over Into
154    because implementing From automatically provides one with an implementation of Into.
155
156    But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types
157    that impl `From<T>`.
158
159    So for the sake of simplicity, You should implement whichever interface you want deal with,
160    Or simply, implement both as the same time.
161
162    Example
163    -------
164    ```py
165    @dataclass
166    class Id(From[str]):
167        value: int
168
169        @classmethod
170        def from_value(cls, value: str) -> Self:
171            return cls(value=int(value))
172
173    ```
174    """
175
176    __slots__ = ()
177
178    @classmethod
179    def from_value(cls, value: T_co) -> Self:
180        """Perform the conversion."""
181        raise NotImplementedError

Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.

As the Rust documentation says, One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into.

But there's no such thing in Python, as it's impossible to auto-impl Into<T> for all types that impl From<T>.

So for the sake of simplicity, You should implement whichever interface you want deal with, Or simply, implement both as the same time.

Example
@dataclass
class Id(From[str]):
    value: int

    @classmethod
    def from_value(cls, value: str) -> Self:
        return cls(value=int(value))

Implementations

This class implements From in Rust.

From(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
@classmethod
def from_value(cls, value: -T_co) -> 'Self':
178    @classmethod
179    def from_value(cls, value: T_co) -> Self:
180        """Perform the conversion."""
181        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryFrom')
@typing.runtime_checkable
class TryFrom(typing.Protocol[-T_co, ~E]):
184@rustc_diagnostic_item("TryFrom")
185@typing.runtime_checkable
186class TryFrom(typing.Protocol[T_co, E]):
187    """Simple and safe type conversions that may fail in a controlled way under some circumstances.
188    It is the reciprocal of `TryInto`.
189
190    It is useful to implement this when you know that the conversion may fail in some way.
191
192    Generic Implementations
193    -------------------
194    This interface takes two type arguments, and return `Result<Self, E>`
195
196    * `T`: Which's the first generic `T` is the type that's being converted from.
197    * `E`: If the conversion fails in a way, this is what will return as the error.
198    * `Self`: Which's the instance of the class that is being converted into.
199
200    Example
201    -------
202    ```py
203    @dataclass
204    class Id(TryFrom[str, str]):
205        value: int
206
207        @classmethod
208        def try_from(cls, value: str) -> Result[Self, str]:
209            if not value.isnumeric():
210                # NaN
211                return Err(f"Couldn't convert: {value} to self")
212            # otherwise convert it to an Id instance.
213            return Ok(value=cls(int(value)))
214    ```
215    """
216
217    __slots__ = ()
218
219    @classmethod
220    def try_from(cls, value: T_co) -> Result[Self, E]:
221        """Perform the conversion."""
222        raise NotImplementedError

Simple and safe type conversions that may fail in a controlled way under some circumstances. It is the reciprocal of TryInto.

It is useful to implement this when you know that the conversion may fail in some way.

Generic Implementations

This interface takes two type arguments, and return Result<Self, E>

  • T: Which's the first generic T is the type that's being converted from.
  • E: If the conversion fails in a way, this is what will return as the error.
  • Self: Which's the instance of the class that is being converted into.
Example
@dataclass
class Id(TryFrom[str, str]):
    value: int

    @classmethod
    def try_from(cls, value: str) -> Result[Self, str]:
        if not value.isnumeric():
            # NaN
            return Err(f"Couldn't convert: {value} to self")
        # otherwise convert it to an Id instance.
        return Ok(value=cls(int(value)))

Implementations

This class implements TryFrom in Rust.

TryFrom(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
@classmethod
def try_from(cls, value: -T_co) -> 'Result[Self, E]':
219    @classmethod
220    def try_from(cls, value: T_co) -> Result[Self, E]:
221        """Perform the conversion."""
222        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryFrom')
@typing.runtime_checkable
class Into(typing.Protocol[+T_cov]):
225@rustc_diagnostic_item("TryFrom")
226@typing.runtime_checkable
227class Into(typing.Protocol[T_cov]):
228    """Conversion from `self`, which may or may not be expensive.
229
230    Example
231    -------
232    ```py
233    @dataclass
234    class Id(Into[str]):
235        value: int
236
237        def into(self) -> str:
238            return str(self.value)
239    ```
240    """
241
242    __slots__ = ()
243
244    def into(self) -> T_cov:
245        """Perform the conversion."""
246        raise NotImplementedError

Conversion from self, which may or may not be expensive.

Example
@dataclass
class Id(Into[str]):
    value: int

    def into(self) -> str:
        return str(self.value)

Implementations

This class implements TryFrom in Rust.

Into(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
def into(self) -> +T_cov:
244    def into(self) -> T_cov:
245        """Perform the conversion."""
246        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryInto')
@typing.runtime_checkable
class TryInto(typing.Protocol[~T, ~E]):
249@rustc_diagnostic_item("TryInto")
250@typing.runtime_checkable
251class TryInto(typing.Protocol[T, E]):
252    """An attempted conversion from `self`, which may or may not be expensive.
253
254    It is useful to implement this when you know that the conversion may fail in some way.
255
256    Generic Implementations
257    -------------------
258    This interface takes two type arguments, and return `Result<T, E>`
259
260    * `T`: The first generic `T` is the type that's being converted into.
261    * `E`: If the conversion fails in a way, this is what will return as the error.
262
263    Example
264    -------
265    ```py
266    @dataclass
267    class Id(TryInto[int, str]):
268        value: str
269
270        def try_into(self) -> Result[int, str]:
271            if not self.value.isnumeric():
272                return Err(f"{self.value} is not a number...")
273            return Ok(int(self.value))
274    ```
275    """
276
277    __slots__ = ()
278
279    def try_into(self) -> Result[T, E]:
280        """Perform the conversion."""
281        raise NotImplementedError

An attempted conversion from self, which may or may not be expensive.

It is useful to implement this when you know that the conversion may fail in some way.

Generic Implementations

This interface takes two type arguments, and return Result<T, E>

  • T: The first generic T is the type that's being converted into.
  • E: If the conversion fails in a way, this is what will return as the error.
Example
@dataclass
class Id(TryInto[int, str]):
    value: str

    def try_into(self) -> Result[int, str]:
        if not self.value.isnumeric():
            return Err(f"{self.value} is not a number...")
        return Ok(int(self.value))

Implementations

This class implements TryInto in Rust.

TryInto(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
def try_into(self) -> 'Result[T, E]':
279    def try_into(self) -> Result[T, E]:
280        """Perform the conversion."""
281        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('ToString')
@typing.runtime_checkable
class ToString(typing.Protocol):
284@rustc_diagnostic_item("ToString")
285@typing.runtime_checkable
286class ToString(typing.Protocol):
287    """A trait for explicitly converting a value to a `str`.
288
289    Example
290    -------
291    ```py
292    class Value[T: bytes](ToString):
293        buffer: T
294
295        def to_string(self) -> str:
296            return self.buffer.decode("utf-8")
297    ```
298    """
299
300    __slots__ = ()
301
302    def to_string(self) -> str:
303        """Converts the given value to a `str`.
304
305        Example
306        --------
307        ```py
308        i = 5  # assume `int` implements `ToString`
309        five = "5"
310        assert five == i.to_string()
311        ```
312        """
313        raise NotImplementedError
314
315    def __str__(self) -> str:
316        return self.to_string()

A trait for explicitly converting a value to a str.

Example
class Value[T: bytes](ToString):
    buffer: T

    def to_string(self) -> str:
        return self.buffer.decode("utf-8")

Implementations

This class implements ToString in Rust.

ToString(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
def to_string(self) -> str:
302    def to_string(self) -> str:
303        """Converts the given value to a `str`.
304
305        Example
306        --------
307        ```py
308        i = 5  # assume `int` implements `ToString`
309        five = "5"
310        assert five == i.to_string()
311        ```
312        """
313        raise NotImplementedError

Converts the given value to a str.

Example
i = 5  # assume `int` implements `ToString`
five = "5"
assert five == i.to_string()