Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

f-string types #811

Open
KotlinIsland opened this issue Nov 7, 2024 · 10 comments
Open

f-string types #811

KotlinIsland opened this issue Nov 7, 2024 · 10 comments
Labels

Comments

@KotlinIsland
Copy link
Owner

KotlinIsland commented Nov 7, 2024

def f[T: str](t: T) -> f"asdf{T}":
    return f"asdf{t}"

reveal_type(f("fdsa"))  # "asdffdsa"
@jorenham
Copy link

jorenham commented Nov 9, 2024

must T be restricted to str, or can it be anything that implements __format__, i.e. T: optype.CanFormat?

@KotlinIsland
Copy link
Owner Author

hmmmmmm, ts only allows string | number | bigint | boolean | null | undefined, even though you can implement a toString, i'm not too sure what to do here...

@jorenham
Copy link

jorenham commented Nov 9, 2024

ehh, are we talking about python?

@jorenham
Copy link

jorenham commented Nov 9, 2024

>>> def f[T: str](t: T) -> f"asdf{T}":
...     return f"asdf{t}"
...     
>>> f.__annotations__
{'t': T, 'return': 'asdfT'}

maybe you can hack around this by monkeypatching TypeVar.__format__, so that it returns something like a DeferredFormat instance, instead of a str.

@KotlinIsland
Copy link
Owner Author

ehh, are we talking about python?

yeah, just referencing prior art

I think ts kinda went with the idea of static string values are allowed, and when there is __str__, __repr__ and __format__ things can get really tricky, if we allow any of these then I suppose:

class A:
   def __format__(self, spec) -> f"A({str})": ...

def f(a: A) -> f"asdf {A}": # same as f"asdf A({str})"
    return f"asdf {a}"

does that mean:

class object:
    def __str__(self) -> f"<{self.__module__}.{self.__class__.__name__} object at {str}>"

@KotlinIsland
Copy link
Owner Author

KotlinIsland commented Nov 10, 2024

>>> def f[T: str](t: T) -> f"asdf{T}":
...     return f"asdf{t}"
...     
>>> f.__annotations__
{'t': T, 'return': 'asdfT'}

maybe you can hack around this by monkeypatching TypeVar.__format__, so that it returns something like a DeferredFormat instance, instead of a str.

don't think that would work for f"{str}"

@jorenham
Copy link

ehh, are we talking about python?

yeah, just referencing prior art

ah I must've read over the "ts" in the comment I was referencing there, mea culpa


don't think that would work for f"{str}"

Yea, good point...


if we allow any of these then I suppose:

class A:
   def __format__(self, spec) -> f"A({str})": ...

def f(a: A) -> f"asdf {A}": # same as f"asdf A({str})"
    return f"asdf {a}"

Yea I suppose that makes sense.

But now that I see this, I realize that this could indeed become very complicated.
Mostly because the returned __format__ could depend on things that aren't known by the typechecker, such as private attributes that are not part of the (generic) type (i.e. as type-params or something).
And then there's also the possibility that __format__ returns a subtype of str, e.g.

>>> class StringSpy(str):
...     def __str__(self):
...         return super().__str__() + " sabotage"
...     def __format__(self, spec):
...         return StringSpy(super().__format__(spec))
...         
>>> f"{StringSpy('nuclear reactor')}"
'nuclear reactor sabotage'
>>> type(_)
<class '__main__.StringSpy'>

So I can see why TS went about it the way they did. I guess that later on __format__ support could always be added. So I guess it might be easiest to start with just the common str-able builtin literals, and then see whether including __format__ is feasible after that.


does that mean:

class object:
    def __str__(self) -> f"<{self.__module__}.{self.__class__.__name__} object at {str}>"

self.__module__ isn't a type in this case 🤔


I haven't yet read it, but at first glance https://peps.python.org/pep-0750/ looks like it might be able to help with this.

@KotlinIsland
Copy link
Owner Author

KotlinIsland commented Nov 10, 2024

self.__module__ isn't a type in this case 🤔

but that's nonsense anyway, that should return str

@jorenham
Copy link

self.__module__ isn't a type in this case 🤔

but that's nonsense anyway, that should return str

but shouldn't it then be

def __str__[ModuleT: str, ClassNameT: str](
    self: HasModule[ModuleT] & HasClass[HasName[ClassNameT]],
) -> f"<{ModuleT}.{ClassNameT} object at {str}>"

@KotlinIsland
Copy link
Owner Author

no, it should be str, because it's subtypes can return any str

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants