TD Stubs in VSCode?

Did anyone get the stubs created and shared by @tekt here
work in VSCode?

Some progress. Copy _stubs folder to your site-packages or project.folder and add the following to the top of your py files

try:
	op
except NameError:
	from _stubs import *

now intellisense will pick up the stubs from init.py
tdu.
UI.
…

Haven’t gotten the other py files (TDFunctions.py ,…) to work yet

Those ones are a bit more awkward because they also need to be imported differently at runtime.

This should work:

try:
	TDF = op.TDModules.mod.TDFunctions
except NameError:
	from _stubs import TDFunctions as TDF

My general practice is to stick the _stubs/ folder inside a lib/ folder in the project, and then include that lib/ folder in the project’s python path (there’s a workspace setting for that in VSCode).

I’ve updated the article with details.

This is sweet, guys. I’ve moved to vscode from pycharm when that code completion project got released. I prefer pycharm, but I’m willing to adapt to what the community is more into, and for whatever reason I haven’t found a lot of other people that like it.

Anyway, let me know if I can help with this stuff. I wrote pretty much all the non-C++ connected Python stuff that’s in here and I’m excited to support evolution into a nice IDE setup.

Yeah, thank you @tekt for creating the stubs! That must have been a lot of work.

I found a few issues when using the stubs in sphinx autodoc. A lot of the classes inherit from typing.Any or typing.Union

class Point(_T.Any):
_AttributeDataTupleT = _T.Union[]
class AttributeData(_AttributeDataTupleT):

which causes sphinx to error

  File "C:\Users\tgallery\Documents\repos\td-milan-design-week-dev\_stubs\__init__.py", line 947, in <module>
    class Point(_T.Any):
  File "c:\users\tgallery\python37\lib\typing.py", line 310, in __new__
    raise TypeError(f"Cannot subclass {cls!r}")
TypeError: Cannot subclass <class 'typing._SpecialForm'>

Looking a the docs confirms that:

You cannot subclass or instantiate a union.

Any ideas?

Most of the uses of Any are mostly just placeholders for things that I haven’t filled out yet.
But in the case of Point and other similar things, it needs to essentially allow any attribute names since all the geometry attributes are accessible through it. I think there’s a way to tell typing “this class definitely has these members but also might have arbitrary other members”, but I haven’t gotten it working.

The _AttributeDataTupleT one was a bit tricky since it has the members of one of 4 different lengths of tuples but also has two other members owner and val.

If anyone wants to contribute to it, send a pull request in Github with your changes.
Otherwise I’ll see what I can do.

I can see how the type hints for attributes and functions are needed, but I still don’t understand why point() and similar classes need to inherit from typing?

Regarding import of TDFunctions and alike. Your suggestion works for intellisense, but if I actually import the module I get a NameError: name 'op' is not defined
for this line TDJ = op.TDModules.mod.TDJSON

As you probably got those automatically from /sys/TDModules we shoudn’t modify those. Any ideas how to fix this one?

Hey there, I’ve just been getting into using stubs based on the article linked and stumbled across this thread mentioning sphinx auto-doc. I’ve been just dipping my toes into that and wondering if anyone here has any practices they’ve been using specifically with touchdesigner to create project docs for internal teams.

Sphinx is a great tool and I’d like to expand it to make docs as readable and thorough as possible for other folks.

Appreciate any tips.

@tekt thank you very much for making this - I am just getting into linting & type checking with TD and I was very happy to find your stubs.

Please may I ask if possibly someone did find a way how to make VSCode import this stuff for every python file in your workspace, without having to actually include this in each file?

from _stubs import *

I am not sure if something like that is even possible (I guess the chances are very little) but it kinda makes sense when thinking about it. I mean every python file in a TD project does already have something like this imported (please correct me if I am wrong):

from td import *

Therefore I was thinking it would be amazing if I could somehow configure VSCode to automagically inject this line for the linters - but without actually including it in the files. Do you think something like that might be possible or is it a bad idea? I have been searching for something like that but with no luck so far.

Adding the import from stubs (with try except block) into every single file (including the all the callbacks) seems a tiny bit painful :confused: Therefore I was thinking whether there might be a way around it.


Also I wanted to ask whether is try except block necessary in case of td stubs? For example if I rename _stubs → td and do the import like this, then it should work both in TD and in VSCode, so I am a bit confused why is the check needed? Would I break something by doing this? Or is it because of some performance reasons?

from td import *

Also if I do it like this, the linter stops yelling at me that op in try except block has some problems (like being unused and unbound).
Thanks!


Edit:
When further thinking about linting and various scenarios where we use something along following lines, I guess there is no way for a linter to know what we are dealing with…

foo = mod( parent().par.Maincomp.eval().op('foo') )
baz = op('bar').Baz

Therefore I feel like the only way to make such scenarios work would be to have some direct (lets say network) connection with TD where TD would tell linter what is what. But I guess that would require quite a lof of work, if even being possible :thinking:
@Ivan please may I ask what you think about future IDE workflows with TD? Are there some plans or thoughts on how should things work in the future?

We’ve been discussing best methods. It’s a tricky problem and we haven’t landed on a solution everyone is happy with.

For a version that uses network connections as you describe, check this out: https://derivative.ca/community-post/asset/td-completes-me-simple-auto-completion-engine-touchdesigner/62836

1 Like

I hope you will find a way that would enable nice IDE workflows :slight_smile:

Thanks, I know about TD-Completes-Me, but as far as I know it doesn’t help with linting (as it is rather an auto-completion tool).

Adding a __builtins__.pyi file in the root of my project directory that contains from _stubs import * gets this working for me in vscode without needing try/except in every file.

Related Pylance Release Notes:

Enhancement: Added the ability to add new symbols to builtins by simply adding a type stub file named __builtins__.pyi locally.

See also:


As an alternative, there’s also a TYPECHECKING constant that can be used to conditionally import types that are only used by the typechecker.

from typing import TYPE_CHECKING
# ...
if TYPE_CHECKING:
  from _stubs import *
4 Likes

@llwt brilliant catch, works like a treat. Thanks for sharing! :raised_hands:

1 Like

@tekt Thank You

just an additional tweak that VSCode will do for you if you use the Ctrl+. option to fix missing imports for TDStoreTools and so on.

Adding the following to /.vscode in the project root adds the stuff not included in init for importing from various files in your tree.

{
    "python.analysis.extraPaths": [
        "./_stubs"
    ]
}

It is very kind of @tekt to maintain this, however, it seems like these stubs could be autogenerated by the build environment that Derivative uses to compile TD and published either on the site or in a repo specifically made for them.

1 Like

NOTE: Is just read @llwt posts and this would probably be the way to go!
I am leaving my rest anyway as it might be interresting.

EDIT:
@llwt OH MY GOD; THIS IS GODSEND!
@tekt Petition to move yout stubs in to its own repository so we can include them as submodules and easier contribute!

Another solution is to rename _stubs in to td and place it in a folder called _stubs
Then, as you said, create/edit .vscode/settings.json with the following

{
    "python.analysis.extraPaths": ["TDImportCache/Lib", "_stubs"]
}

Now, in your py file, call

import td

Good thing is that this will not break TD itself as td is the actual module that just gets autoImported in TD itself.

You can now use td.member or import from TD

from td import * also works

grafik

1 Like

Moving them to a separate repo is a good idea. I’ll get on that.

Sadly it is not as simple a problem as you might think. We are actively working on figuring out a maintainable solution to this. All the work here has been helpful to inform the development, please keep sharing. I personally couldn’t agree more that this is a critical problem for Derivative to solve.

1 Like

I apologize if I was seeming to trivialize someone else’s work. It’s not how it was intended.

No worries. Just understand that robust answers to these things can be much more difficult than they seem. TouchDesigner is amazing for quickly hacking together powerful solutions to specific problems, but creating systems that work reliably with the million and one features of TD does takes some time.

And again, when users make strides on their own and share them, it helps us to kickstart the full built-in features, so keep it coming!