-
Notifications
You must be signed in to change notification settings - Fork 8
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
Add type stubs #46
Add type stubs #46
Conversation
Hmm. I just remembered that SWIG can (sort of) generate type hints. The generated type hints are strings of C types such as The way to get them is to run SWIG without the Not sure if this is helpful given that you've already done the work, though. There is no strong reason for us to be using |
Still helpful, I've give it a shot. Always better to have something autogenerated. Thanks |
In that case it might make sense to remove Feel free to add any necessary build dependencies (mypy?). |
thanks. I'm playing with it right now... removing with getAllowedPropertyValues(...)
getAllowedPropertyValues(CMMCore self, char const * label, char const * propName) -> StrVector
Parameters
----------
label: char const *
propName: char const * without getAllowedPropertyValues(self, label: 'char const *', propName: 'char const *') -> 'std::vector< std::string,std::allocator< std::string > >'
getAllowedPropertyValues(CMMCore self, char const * label, char const * propName) -> StrVector
Parameters
----------
label: char const *
propName: char const * According to the SWIG docs:
but obviously those annotations aren't going to cut it with mypy. so unless I've missed an obvious setting... I may need to add some |
Unfortunately I think that is the best you can currently get from SWIG (swig/swig#735), at least without making the I was thinking that you might be able to do some AST manipulation on the output of mypy |
yeah, that's more or less how I started with the current file. My suspicion (based on a little experience doing this with other projects) is that it's really hard to get something both thorough and accurate without a little manual work. I'll play with a few more things, but my gut feeling at the moment is that we should just go with the current stub, which we can always update in the future if we hit on a better method. I don't get the sense that the signatures in mmcore are changing that much? (and this has no runtime consequences anyway) |
Sounds good to me. Signatures in MMCore should rarely change (except for adding methods), and in any case should cause a major version increment, so we just need to check when switching MMCore versions. |
ok, I'm giving up on the automation for now ... for the sake of record, here's a script that takes the output of so, let me just test that the packaged type hints work as is, and then we can iterate on this in later versions if desired import ast
from ast import Name
import astor
import black
tree = astor.parse_file("out/pymmcore/pymmcore_swig.pyi")
str2type = {
"char": "str",
"std::string": "str",
"unsigned int": "int",
"unsigned long": "int",
"double": "float",
"int": "int",
"long": "int",
"bool": "bool",
"size_t": "int",
"ptrdiff_t": "int",
"void": "None",
"std::map< std::string,std::string >": "Dict[str, str]",
"StrMap": "Dict[str, str]",
"std::istringstream": "StringIO", # probably wrong
"SwigPyIterator": "Any",
"PyObject": "Any",
"swig::SwigPyIterator": "Any",
# not sure?
"DoubleVector": "Sequence[float]",
"LongVector": "Sequence[int]",
"CharVector": "Sequence[str]",
"StrVector": "Sequence[str]",
"std::vector< char >": "Sequence[str]",
"std::vector< std::string >": "Sequence[str]",
"std::vector< long >": "Sequence[int]",
"std::vector< double >": "Sequence[float]",
"std::vector< unsigned int,std::allocator< unsigned int > >": "Sequence[int]",
"std::vector< unsigned char *,std::allocator< unsigned char * > >": "Sequence[str]",
"std::vector< char,std::allocator< char > >": "Sequence=[str]",
"std::vector< long,std::allocator< long > >": "Sequence[int]",
"std::vector< double,std::allocator< double > >": "Sequence[int]",
"std::vector< std::string,std::allocator< std::string > >": "Sequence[str]",
# internal
"CMMError::Code": "int",
"CMMError": "CMMError",
"MM::DeviceType": "int",
"MM::PropertyType": "int",
"MM::DeviceDetectionStatus": "int",
"PropertySetting": "PropertySetting",
"Configuration": "Configuration",
"PropertyPair": "PropertyPair",
"MMEventCallback": "MMEventCallback",
"Metadata": "Metadata",
"MetadataTag": "MetadataTag",
"MetadataSingleTag": "MetadataSingleTag",
"MetadataArrayTag": "MetadataArrayTag",
"PropertyBlock": "PropertyBlock",
}
def _clean_hint(hint):
hint = hint.replace("const", "").rstrip(" &*")
if ">::" in hint:
hint = hint.split(">::")[0] + ">"
return hint
class V(ast.NodeVisitor):
def visit_arg(self, node: ast.arg):
if node.annotation:
if isinstance(node.annotation, ast.Constant):
node.annotation = Name(str2type[_clean_hint(node.annotation.value)])
elif isinstance(node.annotation, ast.Name):
node.annotation = Name(str2type[_clean_hint(node.annotation.id)])
return self.generic_visit(node)
def visit_FunctionDef(self, node: ast.FunctionDef):
if node.returns:
if isinstance(node.returns, ast.Name):
node.returns = Name(str2type[_clean_hint(node.returns.id)])
elif isinstance(node.returns, ast.Constant):
if node.returns.value:
node.returns = Name(str2type[_clean_hint(node.returns.value)])
return self.generic_visit(node)
V().visit(tree)
source = "from typing import Dict, Sequence\n"
source += "from io import StringIO\n\n"
source += astor.to_source(tree)
final = black.format_str(source, mode=black.FileMode(is_pyi=True))
with open("stubs.pyi", "w") as f:
f.write(final) |
ok @marktsuchida, I checked that the stubs get packaged in the wheel, and that mypy and IDEs can see them when the wheel has been installed into a fresh environment. For me, this will be a big quality-of-life improvement as is... but definitely ping me if you run into any issues, or would like to pick up the autogeneration discussion again. I left a comment at the top of the pyi file linking to this issue (and mentioning the script above). thanks! |
Thanks! I actually think your script is pretty good given the constraints we're working with. Maybe I'll take a look at it some time to see if I can resolve some of the question marks. |
closes #43 by adding type stubs to the distribution.
I tried to get a good fully-autogenerated stub file, but alas it was beyond me. So this is partially manually created. (I used the Java stubs, with a fair amount of find and replace, and some manual curation). The possibility for errors exists, but I think it's a way better experience than not having them, and it can always be improved with time.
work-in-progress... I still need to test some things locally to make sure that the built package gets recognized in IDE's and such.