[2025.30060] PyLance not ok with op() return

I set pylance to “default” mode, which results in it enforcing pretty strict type-checking.
It seems like that op() always returns an OP Object, and any kind of OP as a child is not in that category.
@tekt used _AnyOp as a returnType which seemed to mittigate that issue.

1 Like

I’ve encountered that issue as well.
My approach was to define a hierarchy of Union types like _AnyCompT and _AnyDatT as well as _AnyOpT which is a union of those. I used underscores since those aren’t types you can actually use, just for type checking.
Then for things like a “DAT” style parameter, the .eval() method returns _AnyDatT. And the op function returns _AnyOpT.

It’s incomplete and outdated but it’s available here:

Hmph. Thanks for heads up, guys. I will dig into this. Annoying that it won’t let you just force it to a certain type if you know what you’re getting back.

Looking in to it further, op also, potentialy, returns None, which ofc is correct. Maybe there could be a generic OpReturnType that we can easily use to decalre them?

Building that type myself (or using unions here in the typehintin) gets somehwat rid of the error (the type-statement being python 3.12 sadly) but it destroys the detailed type-hints.

We could maybe build something using generics like
op[constantCHOP]( "MyPath" ) ?

Thanks I hate it :smiley: Keep the ideas coming it helps a lot to know what works for you

I would love generics to be the answer as they are pretty concise, I like it. But it seems like python has different ideas about them in regard to function definitions so sadly this also seems to not be the answer.

We could also use the cast method from typing which works similiar to the as keyword in TS and seems to be the cleanest solution at the moment.

Are there any options or overrides for pylance that might help? Strict type checking might just be a no go with TD Python :confused:

I couldn’t figure out how to set up pylance to give this error. The pattern in the original post works fine for me. What is the setting that will set up that level of type checking?


In settings.json set "python.analysis.typeCheckingMode": "standard"

Not sure if I like it as I start doing type gymnastics already but tbh if done right has the potential to save the day :slight_smile:

Okay I dug into this today. I’ve come to the conclusion that if you want to use type narrowing enforcement, you should use typing.cast when necessary, as you suggest. Hacking the definitions of things so that people can get around the type checking that they asked for (by setting standard mode) doesn’t make a lot of sense to me.

If you feel that it’s worth it, we could probably include “cast” into the automatically imported functions.

So I came up with this little utility function:

from typing import cast, TypeVar, Union, TypeGuard, Tuple, Type
T = TypeVar("T")

def is_tuple(value) -> TypeGuard[Tuple]:
	return isinstance( value, tuple )

def opAs(pattern:Union[str, int, Tuple[str, ...]], asType:Type[T], includeUtility = False):
	if is_tuple( pattern ): _pattern = pattern
	else: _pattern = (pattern,)

	return cast( Union[T, None], op(*_pattern, includeUtility = includeUtility))

which looks like this:

foo = op("foo")
# becomes with proper typehinting.
foo = opAs("foo", COMP)

This is cool. I am talking with the team about adding this functionality to the regular op function as an optional argument. So op(“foo”, as=COMP)

Yeah, that sounds nice. (Just noting that it nees to be asType:Type[T] , changed it in the original post)
Alternatively maybe adding an as( opType ) member so it becomes
op("foo").as( COMP ) ( which would only work with opex then as it could return None)
Just thinking loudly :slight_smile:

Coming soon to a build near you

2 Likes

Thats is super lovely, thanks ivan!
Wondering now if we could[should?] go the extra step in also enforcing the type passed in to the asType method and raising an exception if it fails.

My utility-collection also now has an op_as_assert which basicly will raise an Exception if the target operator is not actually of the defined type.

def op_ex_assert(pattern:Union[str, int, Tuple[Union[str, int], ...]], asType:Type[T], includeUtility = False):
	"""
		Uses the opEx method instead of op, so if the target op is None an exceptions raised from the call itself.
	"""
	target_operator = op_as( pattern, asType, includeUtility=includeUtility)
	if not isinstance( target_operator, asType):
		raise WrongOpType(f"Wrong operator type. Expected {asType}, got {type(target_operator)}")
	return target_operator

There is an optional checkType argument that does that. I didn’t put it in that introductory section, but I guess I will.

1 Like

That’s really great, thanks a lot.
Maybe there could be a second method forceType, which does the same but has the Checktype argument True?

This also brings me to another RFE I had a while ago and where I am actually not super sure about an implementation: a standardized way of giving a COMP a kind of signature to ba able to identify the type of it. It would be a dream if this signature, if ever implemented would also be considered in the type check.

I don’t understand what forceType would do in your suggestion here.

As far as “signature” I think you mean tagging of operators to tell if they are of a certain custom build? I have also wanted this… my solution has been to fill a parameter with the “signature”, though I agree that a more standard way will be nice.

forceType would be similiar to how opex exists as a stricter version and raise an exception if the types to not match.
So instead of
opex('constant1').asType(contantTOP, checkType = True)
we would do
opex('constant1').forceType(contantTOP) just to cut down on the checkType argument spamming :wink:

Regarding the “SIgnature” yeah, could also be just a parameter which is guaranteed to exist. Just giving it some form of name which is unified over all customComps.

For now I think we’ll just use checkType argument. Trying not to add to the already very large set of op functions.

I agree about the signature thing. It’s on the list of package system features to add.