De la DLL au Type Hint : dans les coulisses de la génération de stubs pour VSCode (Python) avec Dynamo
Si vous utilisez VSCode (souvent indispensable lorsque l'on a de longs codes), voici un générateur de stubs (.pyi) depuis Dynamo qui reflète l'architecture des espaces de noms .NET et reconstruit la
signature des classes pour les rendre intelligibles par Python,
permettant l'auto-complétion sous VSCode.
-
Quelques points sur les principes de ce générateur
-
Reflection : ce générateur parcourt chaque classe, chaque méthode
et chaque propriété grâce à la Reflection (System.Reflection) en prenant
en compte l'héritage.
-
Métaclasses : permet de gérer des héritages de membres statiques
complexes avec une maitrise des types d'objets retournés et permet une
autocomplétion avancée avec Pylance
- LINQ : génération d'un wrapper spécial pour LINQ avec exemples de syntaxe spécifique pour DynamoPythonNet3 (il vous suffira de copier les exemples de documentation dans VSCode)
À propos de l'utilisation des métaclasses ici plutôt que des
décorateurs @staticmethod ou @classmethod
Nous l'utilisons ici comme une solution de contournement pour Pylance. (le but premier des métaclasses est de personnaliser/créer des classes Python)
- Les décorateurs Python :
@propertyfonctionne uniquement sur une instance (via self). Si l'on fait MaClasse.ma_propriete, Python renverra l'objet property lui-même, pas la valeur.
@classmethodfonctionne pour des méthodes, mais l'IDE attendra que l'on mettes des parenthèses : MaClasse.ma_methode().
- La solution Métaclasse :
Puisque la classe est elle-même une "instance" de sa métaclasse, si on met un
@propertydans la métaclasse, elle devient une propriété réelle pour la classe.
Résultat pour l'IDE : Quand tu tapes Line., l'IDE voit Bound comme une variable (une propriété) et non comme une fonction.
Exemple 1 avec des méthodes d'instances
Exemple 2 avec les méthodes d'extension LINQ
..
-
Le code (PythonNet3)
import os
import clr
import System
import keyword
import textwrap
from System import Reflection
from System.Reflection import BindingFlags
# -----------------------------------------------------------------------------
# LOAD ASSEMBLIES
# -----------------------------------------------------------------------------
assemblies_to_load = [
'RevitAPI',
'RevitAPIUI',
'RevitServices',
'ProtoGeometry',
'DynamoServices',
'RevitNodes',
'System',
'System.Core',
'System.Drawing',
'System.Drawing.Common',
'System.Windows.Forms',
'WindowsBase',
'PresentationCore',
'PresentationFramework',
'System.Xaml',
'UIAutomationTypes',
'UIAutomationProvider',
'WindowsFormsIntegration',
'PresentationUI',
'ReachFramework'
]
for asm_name in assemblies_to_load:
try:
clr.AddReference(asm_name)
except Exception as ex:
print(f'Could not load {asm_name}: {ex}')
from Autodesk.DesignScript.Geometry import *
from Dynamo.Events import *
try:
fullPathScript = ExecutionEvents.ActiveSession.CurrentWorkspacePath
except Exception:
fullPathScript = None
# -----------------------------------------------------------------------------
# CONFIG + MAPPING
# -----------------------------------------------------------------------------
OPERATOR_MAP = {
'op_Equality': '__eq__', 'op_Inequality': '__ne__', 'op_Addition': '__add__',
'op_Subtraction': '__sub__', 'op_Multiply': '__mul__', 'op_Division': '__truediv__',
'op_GreaterThan': '__gt__', 'op_LessThan': '__lt__', 'op_GreaterThanOrEqual': '__ge__',
'op_LessThanOrEqual': '__le__', 'op_UnaryNegation': '__neg__', 'op_UnaryPlus': '__pos__',
'op_BitwiseAnd': '__and__', 'op_BitwiseOr': '__or__', 'op_ExclusiveOr': '__xor__',
'op_LeftShift': '__lshift__', 'op_RightShift': '__rshift__'
}
KW = set(keyword.kwlist)
NL = chr(10)
Q = chr(39) * 3
SYS_MAP = {
'System.String': 'str',
'System.Char': 'str',
'System.Boolean': 'bool',
'System.Int16': 'int',
'System.Int32': 'int',
'System.Int64': 'int',
'System.UInt16': 'int',
'System.UInt32': 'int',
'System.UInt64': 'int',
'System.Byte': 'int',
'System.SByte': 'int',
'System.Single': 'float',
'System.Double': 'float',
'System.Decimal': 'float',
'System.Object': 'object',
'System.Void': 'None',
'System.Type': 'type'
}
SIMPLE = {'Any', 'None', 'object', 'int', 'float', 'str', 'bool', 'bytes', 'type'}
GENERIC_MAP = {
'System.Collections.Generic.IEnumerable`1': 'Iterable',
'System.Collections.Generic.ICollection`1': 'List',
'System.Collections.Generic.IList`1': 'List',
'System.Collections.Generic.List`1': 'List',
'System.Collections.ObjectModel.Collection`1': 'List',
'System.Collections.Generic.IReadOnlyCollection`1': 'Collection',
'System.Collections.Generic.IReadOnlyList`1': 'Sequence',
'System.Collections.ObjectModel.ReadOnlyCollection`1': 'Sequence',
'System.Collections.Generic.HashSet`1': 'Set',
'System.Collections.Generic.ISet`1': 'Set',
'System.Collections.Generic.Dictionary`2': 'Dict',
'System.Collections.Generic.IDictionary`2': 'Dict',
'System.Collections.Generic.IReadOnlyDictionary`2': 'Dict',
'System.Collections.Generic.KeyValuePair`2': 'Tuple',
'System.Nullable`1': 'Optional'
}
_TUPLE_DEFS = set(
[f'System.Tuple`{i}' for i in range(1, 9)] +
[f'System.ValueTuple`{i}' for i in range(1, 9)]
)
_IENUM = 'System.Collections.IEnumerable'
_GIENUM_DEF = 'System.Collections.Generic.IEnumerable`1'
_LINQ_EXCLUDE = {'System.String'}
# -----------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------
def _n_id(n, i=0):
n = n or (f'p{i}')
n = n.replace(' ', '_')
if n in KW:
n += '_'
if n[0].isdigit():
n = 'p_' + n
return n
def _t_name(t):
try:
n = t.Name
return n.split('`')[0] if '`' in n else n
except Exception:
return 'object'
def _t_fqn(t):
try:
fn = t.FullName
if not fn:
return _t_name(t)
if '`' in fn:
fn = fn.split('`')[0]
return fn.replace('+', '.')
except Exception:
return 'object'
def _g_def(t):
try:
if t and t.IsGenericType:
return t.GetGenericTypeDefinition().FullName
except Exception:
pass
return None
def _g_args(t):
try:
a = t.GetGenericArguments()
return [a[i] for i in range(a.Length)]
except Exception:
return []
def _fmt_args(args, ns):
return ', '.join([get_type_hint(x, ns) for x in args])
def _fmt_generic(n, args, ns):
a = _fmt_args(args, ns)
return f'{n}[{a}]' if a else n
def get_type_hint(t, ns=None):
if t is None:
return 'None'
try:
if t.IsByRef:
t = t.GetElementType()
if getattr(t, 'IsGenericParameter', False):
return 'Any'
if getattr(t, 'IsPointer', False):
return 'Any'
if getattr(t, 'IsArray', False):
return f'List[{get_type_hint(t.GetElementType(), ns)}]'
fn = t.FullName
if fn in SYS_MAP:
return SYS_MAP[fn]
if fn == 'System.Action':
return 'Callable[[], None]'
gd = _g_def(t)
if gd:
ga = _g_args(t)
if gd in GENERIC_MAP:
return _fmt_generic(GENERIC_MAP[gd], ga, ns)
if gd in _TUPLE_DEFS:
return _fmt_generic('Tuple', ga, ns)
if gd == 'System.Predicate`1' and len(ga) == 1:
return f'Callable[[{get_type_hint(ga[0], ns)}], bool]'
if gd.startswith('System.Func`') and len(ga) > 0:
ps = _fmt_args(ga[:-1], ns)
return f'Callable[[{ps}], {get_type_hint(ga[-1], ns)}]'
if gd.startswith('System.Action`'):
return f'Callable[[{_fmt_args(ga, ns)}], None]'
return f'{_t_fqn(t)}[{_fmt_args(ga, ns)}]'
if ns and t.Namespace == ns:
return _t_name(t)
return _t_fqn(t)
except Exception:
return 'object'
def ann(h):
if not h:
return 'Any'
return h if h in SIMPLE else repr(h)
def _pi_kind(pi):
try:
if pi.IsOut:
return 'out'
if pi.ParameterType and pi.ParameterType.IsByRef:
return 'ref'
except Exception:
pass
return 'in'
def _sig_params(ps, ns):
a, d = [], []
for i in range(ps.Length):
p = ps[i]
n = _n_id(p.Name, i)
k = _pi_kind(p)
t = get_type_hint(p.ParameterType, ns)
s = f'{n}: {ann(t)}'
try:
if p.IsOptional:
s += ' = ...'
except Exception:
pass
a.append(s)
d.append((n, t, k))
return a, d
def _doc_args(d):
if not d:
return 'Args:' + NL + ' (none)'
ls = ['Args:']
for n, t, k in d:
ks = '' if k == 'in' else f' [{k}]'
ls.append(f' {n} ({t}):{ks}')
return NL.join(ls)
def _doc_ret(r):
return f'Returns:{NL} {r}'
def _doc_overloads(mis, ns, t_self=None, lim=12):
ls, c = ['Overloads:'], 0
for mi in mis:
if c >= lim:
ls.append(' ...')
break
ps = mi.GetParameters()
a, _ = _sig_params(ps, ns)
try:
if getattr(mi, 'IsConstructor', False):
rt = 'None'
elif t_self is not None and mi.ReturnType == mi.DeclaringType:
rt = t_self
else:
rt = get_type_hint(mi.ReturnType, ns)
except Exception:
rt = 'object'
ls.append(f" ({', '.join(a)}) -> {rt}")
c += 1
return NL.join(ls)
def _ienum_elem(t):
try:
if t is None:
return None
fn = getattr(t, 'FullName', None)
if fn in _LINQ_EXCLUDE:
return None
if getattr(t, 'IsArray', False):
return t.GetElementType()
if _g_def(t) == _GIENUM_DEF:
ga = _g_args(t)
return ga[0] if ga else System.Object
if hasattr(t, 'GetInterfaces'):
for it in t.GetInterfaces():
if _g_def(it) == _GIENUM_DEF:
ga = _g_args(it)
return ga[0] if ga else System.Object
for it in t.GetInterfaces():
try:
if it.FullName == _IENUM:
return System.Object
except Exception:
pass
if fn == _IENUM:
return System.Object
except Exception:
pass
return None
def _linq_type_hint(t, ns, quote_arg=False):
et = _ienum_elem(t)
if et is None:
return None
h = get_type_hint(et, ns)
if quote_arg and h not in SIMPLE:
h = repr(h)
return f'IEnumerableLinq[{h}]'
def _ret_hint(rt_type, ns, tself, decl_type):
try:
if rt_type == decl_type:
return tself
except Exception:
pass
lq = _linq_type_hint(rt_type, ns)
return lq if lq else get_type_hint(rt_type, ns)
def _base_hint(t, ns):
lq = _linq_type_hint(t, ns, True)
return lq if lq else 'object'
def _fp_self(a):
return ', '.join(['self'] + a)
def _fp_static(a):
return ', '.join(a)
def _fp_meta(a):
return ', '.join(['cls'] + a)
def _write_doc(f, txt):
f.write(' ' + Q + txt + Q + NL)
def _has_index_params(p):
try:
return p.GetIndexParameters().Length > 0
except Exception:
return False
def _is_static_property(p):
try:
gm = p.GetGetMethod()
sm = p.GetSetMethod()
return (gm is not None and gm.IsStatic) or (sm is not None and sm.IsStatic)
except Exception:
return False
def _meta_name(tn):
return _n_id(f'_{tn}Static')
def _write_static_meta(f, t, ns, max_ov=8, max_doc_ov=12):
flags = BindingFlags.Public | BindingFlags.Static
tn = _t_name(t)
mn_meta = _meta_name(tn)
sprops = [p for p in t.GetProperties(flags) if not _has_index_params(p) and _is_static_property(p)]
sms = [m for m in t.GetMethods(flags) if not (m.IsSpecialName and not m.Name.startswith('op_'))]
sms = [m for m in sms if not OPERATOR_MAP.get(m.Name, m.Name).startswith('__')]
if not sprops and not sms:
return None
f.write(f'class {mn_meta}(type):' + NL)
wrote = False
for p in sorted(sprops, key=lambda x: x.Name):
pn = _n_id(p.Name)
rt = _ret_hint(p.PropertyType, ns, tn, t)
wrote = True
f.write(' @property' + NL)
f.write(f' def {pn}(cls) -> {ann(rt)}:' + NL)
_write_doc(f, _doc_ret(rt))
f.write(' ...' + NL)
grp = {}
for m in sms:
grp.setdefault(OPERATOR_MAP.get(m.Name, m.Name), []).append(m)
for mn, mis in sorted(grp.items(), key=lambda x: x[0]):
wrote = True
if len(mis) == 1:
mi = mis[0]
a, d = _sig_params(mi.GetParameters(), ns)
rt = _ret_hint(mi.ReturnType, ns, tn, mi.DeclaringType)
f.write(f' def {mn}({_fp_meta(a)}) -> {ann(rt)}:' + NL)
_write_doc(f, _doc_args(d) + NL + _doc_ret(rt))
f.write(' ...' + NL)
else:
lim = min(len(mis), max_ov)
for i in range(lim):
mi = mis[i]
a, d = _sig_params(mi.GetParameters(), ns)
rt = _ret_hint(mi.ReturnType, ns, tn, mi.DeclaringType)
f.write(' @overload' + NL)
f.write(f' def {mn}({_fp_meta(a)}) -> {ann(rt)}:' + NL)
_write_doc(f, _doc_args(d) + NL + _doc_ret(rt))
f.write(' ...' + NL)
if len(mis) > max_ov:
rt0 = _ret_hint(mis[0].ReturnType, ns, tn, mis[0].DeclaringType)
f.write(f' def {mn}(cls, *args: Any, **kwargs: Any) -> {ann(rt0)}:' + NL)
_write_doc(f, _doc_args([]) + NL + _doc_ret(rt0) + NL + _doc_overloads(mis, ns, tn, max_doc_ov))
f.write(' ...' + NL)
if not wrote:
f.write(' ...' + NL)
f.write(NL)
return mn_meta
# -----------------------------------------------------------------------------
# MAIN CLASS GENERATOR STUB
# -----------------------------------------------------------------------------
class GlobalStubGenerator:
def __init__(self, root_path):
self.root = root_path
self.folders = {
'full': os.path.join(root_path, 'stubs'),
'mini': os.path.join(root_path, 'stubs.mini')
}
self.ns_data, self.ns_assemblies = {}, {}
for p in self.folders.values():
if not os.path.exists(p):
os.makedirs(p)
def _ensure_pkg_tree(self, base_path, rel_dir):
cur = base_path
for p in [x for x in rel_dir.split(os.sep) if x]:
cur = os.path.join(cur, p)
if not os.path.exists(cur):
os.makedirs(cur)
ip = os.path.join(cur, '__init__.pyi')
if not os.path.exists(ip):
with open(ip, 'w', encoding='utf-8') as f:
f.write('')
def _load_assembly(self, asm_in):
if not isinstance(asm_in, str):
return asm_in
try:
return Reflection.Assembly.Load(asm_in)
except Exception:
try:
for a in System.AppDomain.CurrentDomain.GetAssemblies():
if a.GetName().Name == asm_in:
return a
except Exception:
pass
return None
def collect_from_assembly(self, asm_in):
asm = self._load_assembly(asm_in)
if asm is None:
return
try:
ts = asm.GetExportedTypes()
except Exception as ex:
try:
ts = ex.Types
except Exception:
return
for t in ts:
if t is None or not t.Namespace:
continue
ks = self.ns_data.setdefault(t.Namespace, {})
ks[_t_fqn(t)] = t
self.ns_assemblies.setdefault(t.Namespace, set()).add(asm.FullName)
def write_all(self, max_ov=8, max_doc_ov=12):
for ns, tdct in self.ns_data.items():
ts = list(tdct.values())
rel_dir = os.path.join(*ns.split('.'))
asm_info = ', '.join(sorted(list(self.ns_assemblies[ns])))
hdr = (
f'# encoding: utf-8{NL}'
f'# stub module {ns}{NL}'
f'# from {asm_info}{NL}{NL}'
f'from __future__ import annotations{NL}'
f'from typing import Any, Callable, Collection, Dict, Iterable, Iterator, '
f'List, Optional, Protocol, Sequence, Set, Tuple, TypeVar, Generic, overload{NL}'
f'from _linq import IEnumerableLinq, IOrderedEnumerableLinq, IGroupingLinq{NL}{NL}'
)
for _, base in self.folders.items():
self._ensure_pkg_tree(base, rel_dir)
td = os.path.join(base, rel_dir)
with open(os.path.join(td, '__init__.pyi'), 'w', encoding='utf-8') as f:
f.write(hdr)
f.write('class IDisposable: ...' + NL)
f.write('class IGraphicItem: ...' + NL)
f.write('class IInstanceableGraphicItem: ...' + NL)
f.write('class DesignScriptEntity: ...' + NL)
f.write('class Enum: ...' + NL + NL)
for t in sorted(ts, key=lambda x: x.Name):
if not (t.IsClass or t.IsInterface or t.IsValueType or t.IsEnum):
continue
tn = _t_name(t)
if t.IsEnum:
f.write(f'class {tn}(Enum):' + NL)
fs = t.GetFields(BindingFlags.Public | BindingFlags.Static)
wrote = False
for fi in fs:
if fi.Name == 'value__':
continue
f.write(f' {_n_id(fi.Name)}: Any' + NL)
wrote = True
if not wrote:
f.write(' ...' + NL)
f.write(NL)
continue
smeta = _write_static_meta(f, t, ns, max_ov, max_doc_ov)
base_hint = _base_hint(t, ns)
if smeta:
f.write(f'class {tn}({base_hint}, metaclass={smeta}):' + NL)
else:
f.write(f'class {tn}({base_hint}):' + NL)
wrote = False
cs = t.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
if cs is not None and cs.Length > 0:
wrote = True
if cs.Length == 1:
a, d = _sig_params(cs[0].GetParameters(), ns)
f.write(f' def __init__({_fp_self(a)}) -> None:' + NL)
_write_doc(f, _doc_args(d) + NL + _doc_ret('None'))
f.write(' ...' + NL)
else:
lim = min(cs.Length, max_ov)
for i in range(lim):
a, d = _sig_params(cs[i].GetParameters(), ns)
f.write(' @overload' + NL)
f.write(f' def __init__({_fp_self(a)}) -> None:' + NL)
_write_doc(f, _doc_args(d) + NL + _doc_ret('None'))
f.write(' ...' + NL)
if cs.Length > max_ov:
f.write(' def __init__(self, *args: Any) -> None:' + NL)
_write_doc(f, _doc_args([]) + NL + _doc_ret('None') + NL + _doc_overloads(cs, ns, None, max_doc_ov))
f.write(' ...' + NL)
iflags = BindingFlags.Public | BindingFlags.Instance
aflags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static
for p in t.GetProperties(iflags):
if _has_index_params(p) or _is_static_property(p):
continue
pn = _n_id(p.Name)
rt = _ret_hint(p.PropertyType, ns, tn, t)
sm = p.GetSetMethod()
wrote = True
f.write(' @property' + NL)
f.write(f' def {pn}(self) -> {ann(rt)}:' + NL)
_write_doc(f, _doc_ret(rt))
f.write(' ...' + NL)
if sm is not None:
f.write(f' @{pn}.setter' + NL)
f.write(f' def {pn}(self, v: {ann(rt)}) -> None:' + NL)
_write_doc(f, _doc_args([('v', rt, 'in')]) + NL + _doc_ret('None'))
f.write(' ...' + NL)
ms = [m for m in t.GetMethods(aflags) if not (m.IsSpecialName and not m.Name.startswith('op_'))]
ms = [m for m in ms if (not m.IsStatic) or OPERATOR_MAP.get(m.Name, m.Name).startswith('__')]
grp = {}
for m in ms:
n = OPERATOR_MAP.get(m.Name, m.Name)
k = (n, m.IsStatic)
grp.setdefault(k, []).append(m)
for (mn, is_static), mis in sorted(grp.items(), key=lambda x: (x[0][0], x[0][1])):
wrote = True
tself = tn
if len(mis) == 1:
mi = mis[0]
a, d = _sig_params(mi.GetParameters(), ns)
rt = _ret_hint(mi.ReturnType, ns, tself, mi.DeclaringType)
if is_static and not mn.startswith('__'):
f.write(' @staticmethod' + NL)
f.write(f' def {mn}({_fp_static(a)}) -> {ann(rt)}:' + NL)
else:
fp = _fp_self(a) if not is_static else _fp_static(a)
f.write(f' def {mn}({fp}) -> {ann(rt)}:' + NL)
_write_doc(f, _doc_args(d) + NL + _doc_ret(rt))
f.write(' ...' + NL)
else:
lim = min(len(mis), max_ov)
for i in range(lim):
mi = mis[i]
a, d = _sig_params(mi.GetParameters(), ns)
rt = _ret_hint(mi.ReturnType, ns, tself, mi.DeclaringType)
f.write(' @overload' + NL)
if is_static and not mn.startswith('__'):
f.write(' @staticmethod' + NL)
f.write(f' def {mn}({_fp_static(a)}) -> {ann(rt)}:' + NL)
else:
fp = _fp_self(a) if not is_static else _fp_static(a)
f.write(f' def {mn}({fp}) -> {ann(rt)}:' + NL)
_write_doc(f, _doc_args(d) + NL + _doc_ret(rt))
f.write(' ...' + NL)
if len(mis) > max_ov:
rt0 = _ret_hint(mis[0].ReturnType, ns, tself, mis[0].DeclaringType)
if is_static and not mn.startswith('__'):
f.write(' @staticmethod' + NL)
f.write(f' def {mn}(*args: Any, **kwargs: Any) -> {ann(rt0)}:' + NL)
else:
f.write(f' def {mn}(self, *args: Any, **kwargs: Any) -> {ann(rt0)}:' + NL)
_write_doc(f, _doc_args([]) + NL + _doc_ret(rt0) + NL + _doc_overloads(mis, ns, tself, max_doc_ov))
f.write(' ...' + NL)
if not wrote:
f.write(' ...' + NL)
f.write(NL)
def create_clr_stub(self):
c = (
f"# encoding: utf-8{NL}"
f"# stub module clr{NL}"
f"from typing import Any{NL}{NL}"
f"def AddReference(name: str) -> Any: ...{NL}"
)
for m in ['stubs', 'stubs.mini']:
with open(os.path.join(self.root, m, 'clr.pyi'), 'w', encoding='utf-8') as f:
f.write(c)
def create_linq_stub(self):
lq = textwrap.dedent('''\
# encoding: utf-8
# stub helpers for CLR LINQ extension methods
from __future__ import annotations
from typing import Any, Callable, Generic, Iterable, Iterator, List, Optional, Protocol, Sequence, TypeVar, overload
T = TypeVar('T')
U = TypeVar('U')
K = TypeVar('K')
class _Where(Protocol, Generic[T]):
@overload
def __call__(self, predicate: Callable[[T], bool]) -> 'IEnumerableLinq[T]':
"""Code syntax for Dynamo PythonNet3:\n
.Where(System.Func[System.Object, System.Boolean](lambda x: x.Condition))
\nReturns:
IEnumerableLinq[T]"""
...
@overload
def __call__(self, predicate: Any) -> 'IEnumerableLinq[Any]':
"""Code syntax for Dynamo PythonNet3:\n
.Where(System.Func[System.Object, System.Boolean](lambda x: ...))
\nReturns:
IEnumerableLinq[Any]"""
...
@overload
def __getitem__(self, types: Any) -> Callable[[Callable[[T], bool]], 'IEnumerableLinq[T]']: ...
@overload
def __getitem__(self, types: Any) -> Callable[[Any], 'IEnumerableLinq[Any]']: ...
class _Select(Protocol, Generic[T]):
@overload
def __call__(self, selector: Callable[[T], U]) -> 'IEnumerableLinq[U]':
"""Code syntax for Dynamo PythonNet3:\n
.Select[System.Object, System.Object](System.Func[System.Object, System.Object](lambda x: x.Property))
\nReturns:
IEnumerableLinq[U]"""
...
@overload
def __call__(self, selector: Any) -> 'IEnumerableLinq[Any]':
"""Code syntax for Dynamo PythonNet3:\n
.Select[System.Object, System.Object](System.Func[System.Object, System.Object](lambda x: x.Property)) Returns:
IEnumerableLinq[Any]"""
...
@overload
def __getitem__(self, types: Any) -> Callable[[Callable[[T], U]], 'IEnumerableLinq[U]']: ...
@overload
def __getitem__(self, types: Any) -> Callable[[Any], 'IEnumerableLinq[Any]']: ...
class _SelectMany(Protocol, Generic[T]):
@overload
def __call__(self, selector: Callable[[T], Iterable[U]]) -> 'IEnumerableLinq[U]':
"""Code syntax for Dynamo PythonNet3:\n
.SelectMany[System.Object, System.Object](System.Func[System.Object, System.Collections.Generic.IEnumerable[System.Object]](lambda x: x.Items))
\nReturns:
IEnumerableLinq[U]"""
...
@overload
def __call__(self, selector: Any) -> 'IEnumerableLinq[Any]':
"""Code syntax for Dynamo PythonNet3:\n
.SelectMany[System.Object, System.Object](System.Func[System.Object, System.Object](lambda x: x.Items))
\nReturns:
IEnumerableLinq[Any]"""
...
@overload
def __getitem__(self, types: Any) -> Callable[[Callable[[T], Iterable[U]]], 'IEnumerableLinq[U]']: ...
@overload
def __getitem__(self, types: Any) -> Callable[[Any], 'IEnumerableLinq[Any]']: ...
class _OrderBy(Protocol, Generic[T]):
@overload
def __call__(self, keySelector: Callable[[T], K]) -> 'IOrderedEnumerableLinq[T]':
"""Code syntax for Dynamo PythonNet3:\n
.OrderBy[System.Object, System.String](System.Func[System.Object, System.String](lambda x: x.Key))
\nReturns:
IOrderedEnumerableLinq[T]"""
...
@overload
def __call__(self, keySelector: Any) -> 'IOrderedEnumerableLinq[Any]':
"""Code syntax for Dynamo PythonNet3:\n
.OrderBy[System.Object, System.String](System.Func[System.Object, System.String](lambda x: x.Key))
\nReturns:
IOrderedEnumerableLinq[Any]"""
...
@overload
def __getitem__(self, types: Any) -> Callable[[Callable[[T], K]], 'IOrderedEnumerableLinq[T]']: ...
@overload
def __getitem__(self, types: Any) -> Callable[[Any], 'IOrderedEnumerableLinq[Any]']: ...
class _GroupBy(Protocol, Generic[T]):
@overload
def __call__(self, keySelector: Callable[[T], K]) -> 'IEnumerableLinq[IGroupingLinq[K, T]]':
"""Code syntax for Dynamo PythonNet3:\n
.GroupBy[System.Object, System.String](System.Func[System.Object, System.String](lambda x: x.Key))
\nReturns:
IEnumerableLinq[IGroupingLinq[K, T]]"""
...
@overload
def __call__(self, keySelector: Any) -> 'IEnumerableLinq[IGroupingLinq[Any, Any]]':
"""Code syntax for Dynamo PythonNet3:\n
.GroupBy[System.Object, System.String](System.Func[System.Object, System.String](lambda x: x.Key))
\nReturns:
IEnumerableLinq[IGroupingLinq[Any, Any]]"""
...
@overload
def __getitem__(self, types: Any) -> Callable[[Callable[[T], K]], 'IEnumerableLinq[IGroupingLinq[K, T]]']: ...
@overload
def __getitem__(self, types: Any) -> Callable[[Any], 'IEnumerableLinq[IGroupingLinq[Any, Any]]']: ...
class IEnumerableLinq(Generic[T]):
# callable attributes (supporting obj.Method[...] syntax)
Where: _Where[T]
Select: _Select[T]
SelectMany: _SelectMany[T]
OrderBy: _OrderBy[T]
OrderByDescending: _OrderBy[T]
GroupBy: _GroupBy[T]
def __iter__(self) -> Iterator[T]: ...
def Any(self, predicate: Optional[Callable[[T], bool]] = None) -> bool:
"""Code syntax for Dynamo PythonNet3:
.Any()
.Any(System.Func[System.Object, System.Boolean](lambda x: x == value))
\nReturns:
bool"""
...
def All(self, predicate: Callable[[T], bool]) -> bool:
"""Code syntax for Dynamo PythonNet3:
.All(System.Func[System.Object, System.Boolean](lambda x: x.IsValid))
\nReturns:
bool"""
...
def First(self, predicate: Optional[Callable[[T], bool]] = None) -> T:
"""Code syntax for Dynamo PythonNet3:
.First()
.First(System.Func[System.Object, System.Boolean](lambda x: x.Id == 1))
\nReturns:
T"""
...
def FirstOrDefault(self, predicate: Optional[Callable[[T], bool]] = None) -> Optional[T]:
"""Code syntax for Dynamo PythonNet3:
.FirstOrDefault()
.FirstOrDefault(System.Func[System.Object, System.Boolean](lambda x: x.Name == "item"))
\nReturns:
Optional[T]"""
...
def Last(self, predicate: Optional[Callable[[T], bool]] = None) -> T:
"""Code syntax for Dynamo PythonNet3:
.Last()
.Last(System.Func[System.Object, System.Boolean](lambda x: x.Value))
\nReturns:
T"""
...
def LastOrDefault(self, predicate: Optional[Callable[[T], bool]] = None) -> Optional[T]:
"""Code syntax for Dynamo PythonNet3:
.LastOrDefault()
.LastOrDefault(System.Func[System.Object, System.Boolean](lambda x: x.Id == 1))
\nReturns:
Optional[T]"""
...
def Single(self, predicate: Optional[Callable[[T], bool]] = None) -> T:
"""Code syntax for Dynamo PythonNet3:
.Single()
.Single(System.Func[System.Object, System.Boolean](lambda x: x.Id == 1))
\nReturns:
T"""
...
def SingleOrDefault(self, predicate: Optional[Callable[[T], bool]] = None) -> Optional[T]:
"""Code syntax for Dynamo PythonNet3:
.SingleOrDefault()
.SingleOrDefault(System.Func[System.Object, System.Boolean](lambda x: x.Id == 1))
\nReturns:
Optional[T]"""
...
def Count(self, predicate: Optional[Callable[[T], bool]] = None) -> int:
"""Code syntax for Dynamo PythonNet3:
.Count()
.Count(System.Func[System.Object, System.Boolean](lambda x: x.Id == 1))
\nReturns:
int"""
...
def ToList(self) -> List[T]:
"""Returns:
List[T]"""
...
def ToArray(self) -> Sequence[T]:
"""Returns:
Sequence[T]"""
...
def Cast(self, tp: Any) -> 'IEnumerableLinq[Any]':
"""Code syntax for Dynamo PythonNet3:
.Cast(SomeClrType)
\nReturns:
IEnumerableLinq[Any]"""
...
def OfType(self, tp: Any) -> 'IEnumerableLinq[Any]':
"""Code syntax for Dynamo PythonNet3:
.OfType(SomeClrType)
\nReturns:
IEnumerableLinq[Any]"""
...
def Distinct(self) -> 'IEnumerableLinq[T]':
"""Code syntax for Dynamo PythonNet3:
.Distinct()
\nReturns:
IEnumerableLinq[T]"""
...
def Take(self, count: int) -> 'IEnumerableLinq[T]':
"""Code syntax for Dynamo PythonNet3:
.Take(10)
\nReturns:
IEnumerableLinq[T]"""
...
def Skip(self, count: int) -> 'IEnumerableLinq[T]':
"""Code syntax for Dynamo PythonNet3:
.Skip(10)
\nReturns:
IEnumerableLinq[T]"""
...
class IGroupingLinq(IEnumerableLinq[T], Generic[K, T]):
@property
def Key(self) -> K:
"""Returns:
K"""
...
class IOrderedEnumerableLinq(IEnumerableLinq[T]):
def ThenBy(self, keySelector: Callable[[T], K]) -> 'IOrderedEnumerableLinq[T]':
"""Code syntax for Dynamo PythonNet3:
.ThenBy(System.Func[System.Object, System.Object](lambda x: x.Key))
\nReturns:
IOrderedEnumerableLinq[T]"""
...
def ThenByDescending(self, keySelector: Callable[[T], K]) -> 'IOrderedEnumerableLinq[T]':
"""Code syntax for Dynamo PythonNet3:
.ThenByDescending(System.Func[System.Object, System.Object](lambda x: x.Key))
\nReturns:
IOrderedEnumerableLinq[T]"""
...
'''
)
for m in ['stubs', 'stubs.mini']:
with open(os.path.join(self.root, m, '_linq.pyi'), 'w', encoding='utf-8') as f:
f.write(lq)
# -----------------------------------------------------------------------------
# START HERE
# -----------------------------------------------------------------------------
out_path = IN[0]
if not System.IO.Directory.Exists(out_path):
out_path = System.IO.Path.GetDirectoryName(fullPathScript)
gen = GlobalStubGenerator(out_path)
gen.collect_from_assembly(Reflection.Assembly.GetAssembly(System.Object))
for a in assemblies_to_load:
gen.collect_from_assembly(a)
gen.write_all(max_ov=8, max_doc_ov=12)
gen.create_clr_stub()
gen.create_linq_stub()
OUT = 'Success! .pyi stubs generated'
-
Utilisation :
-
Copier le code dans un nœud PytonNet3 avec en entrée le chemin du
dossier où sera généré le stub.
-
Éditer la liste des assemblies à
"générer" assemblies_to_load (optionnel)
-
Sauvegarder et lancer le script
-
Rajouter les chemins du dossier stubs dans VSCode comme ceci
(raccourci Ctrl + , ).
{
"python.analysis.fixAll": [],
"python.analysis.extraPaths": [
"D:/MyFolder/01 Dynamo Stubs generator for VSCode/stubs"
],
//"python.analysis.typeCheckingMode": "basic",
"python.analysis.stubPath": "D:/MyFolder/01 Dynamo Stubs generator for VSCode/stubs",
"python.analysis.diagnosticSeverityOverrides": {},
"python.analysis.aiCodeActions": {
"implementAbstractClasses": true,
"generateSymbol": true,
"convertFormatString": true
}
}
-
Rappel sur les types hints (cast)
Bien que Python soit dynamiquement typé, il peut être utile de
déclarer (caster) le type de certaines variables pour
l'autocomplétion :
-
soit via le Type Hinting (annotations)
-
soit avec l'utilisation de
typing.cast
Exemple avec le Type Hinting - soit via le Type Hinting (annotations)
-
soit avec l'utilisation de
typing.cast
## EXAMPLE 1 ##
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc : DB.Document = DocumentManager.Instance.CurrentDBDocument
# Now, when typing "doc.", the IDE will suggest .Export(), .GetElement(), etc.
## EXAMPLE 2 ##
from Autodesk.Revit.DB import Wall
walls = FilteredElementCollector(doc).OfClass(Wall).ToElements()
for wall in walls:
# type hinting
wall: Wall
# autocompletion enable on wall
print(wall.Width)
Exemple avec typing.cast
from typing import cast
from Autodesk.Revit.DB import Wall
for item in walls:
# typing.cast
wall = cast(Wall, item)







0 commentaires:
Enregistrer un commentaire