mirror of https://github.com/KLayout/klayout.git
Merge branch 'python-typehints'
This commit is contained in:
commit
1edbd6232f
|
|
@ -68,3 +68,5 @@ dist/
|
|||
# Macos artifacts
|
||||
*.dmg
|
||||
*.dmg.md5
|
||||
.DS_Store
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
# Generates LVS and DRC documentation
|
||||
#
|
||||
# Run this script from a valid build below the repository root, e.g.
|
||||
# <repo-root>/build-debug. It needs to have a "klayout" binary in
|
||||
# current directory.
|
||||
|
||||
inst=$(realpath $(dirname $0))
|
||||
scripts=${inst}/drc_lvs_doc
|
||||
ld=$(realpath .)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
# Generates LVS and DRC documentation
|
||||
#
|
||||
# Run this script from a valid build below the repository root, e.g.
|
||||
# <repo-root>/build-debug. It needs to have a "pymod" installation in
|
||||
# current directory.
|
||||
|
||||
inst=$(realpath $(dirname $0))
|
||||
scripts=${inst}/drc_lvs_doc
|
||||
ld=$(realpath .)
|
||||
|
||||
pymod="$ld/pymod"
|
||||
|
||||
export LD_LIBRARY_PATH=$ld
|
||||
export PYTHONPATH=$pymod
|
||||
|
||||
pymod_src=$ld/../src/pymod
|
||||
if ! [ -e $pymod_src ]; then
|
||||
echo "*** ERROR: missing pymod sources ($pymod_src) - did you run the script from the build folder below the source tree?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -e $pymod ]; then
|
||||
echo "*** ERROR: missing pymod folder ($pymod) - did you run the script from the build folder?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python=
|
||||
for try_python in python python3; do
|
||||
if $try_python -c "import klayout.tl" >/dev/null 2>&1; then
|
||||
python=$try_python
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$python" = "" ]; then
|
||||
echo "*** ERROR: no functional python or pymod installation found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating stubs for tl .."
|
||||
$python $inst/stubgen.py tl >$pymod_src/distutils_src/klayout/tlcore.pyi
|
||||
|
||||
echo "Generating stubs for db .."
|
||||
$python $inst/stubgen.py db tl >$pymod_src/distutils_src/klayout/dbcore.pyi
|
||||
|
||||
echo "Generating stubs for rdb .."
|
||||
$python $inst/stubgen.py rdb tl,db >$pymod_src/distutils_src/klayout/rdbcore.pyi
|
||||
|
||||
echo "Generating stubs for lay .."
|
||||
$python $inst/stubgen.py lay tl,db,rdb >$pymod_src/distutils_src/klayout/laycore.pyi
|
||||
|
||||
echo "Generating stubs for lib .."
|
||||
$python $inst/stubgen.py lib tl,db >$pymod_src/distutils_src/klayout/libcore.pyi
|
||||
|
||||
echo "Done."
|
||||
|
||||
|
|
@ -0,0 +1,465 @@
|
|||
""" Stub file generation routines.
|
||||
|
||||
This module contains routines to generate stub files from klayout's python API.
|
||||
This uses the `tl` module of the API, which offers an introspection layer to
|
||||
the C-extension modules.
|
||||
"""
|
||||
|
||||
from collections import Counter
|
||||
from copy import copy
|
||||
from dataclasses import dataclass, field
|
||||
from functools import wraps
|
||||
import functools
|
||||
from sys import argv
|
||||
from textwrap import indent
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
import pya # initialize all modules
|
||||
import klayout.tl as ktl
|
||||
|
||||
|
||||
def qualified_name(_class: ktl.Class) -> str:
|
||||
name = _class.name()
|
||||
if _class.parent():
|
||||
return f"{qualified_name(_class.parent())}.{name}"
|
||||
else:
|
||||
return name
|
||||
|
||||
|
||||
def superclass(_class: ktl.Class) -> str:
|
||||
if _class.base():
|
||||
return superclass(_class.base())
|
||||
else:
|
||||
return _class.name()
|
||||
|
||||
|
||||
def is_reserved_word(name: str) -> bool:
|
||||
wordlist = [
|
||||
"and",
|
||||
"del",
|
||||
"from",
|
||||
"not",
|
||||
"while",
|
||||
"as",
|
||||
"elif",
|
||||
"global",
|
||||
"or",
|
||||
"with",
|
||||
"assert",
|
||||
"else",
|
||||
"if",
|
||||
"pass",
|
||||
"yield",
|
||||
"break",
|
||||
"except",
|
||||
"import",
|
||||
"print",
|
||||
"class",
|
||||
"exec",
|
||||
"in",
|
||||
"raise",
|
||||
"continue",
|
||||
"finally",
|
||||
"is",
|
||||
"return",
|
||||
"def",
|
||||
"for",
|
||||
"lambda",
|
||||
"try",
|
||||
"None",
|
||||
]
|
||||
return name in wordlist
|
||||
|
||||
|
||||
_type_dict = dict()
|
||||
|
||||
_type_dict[ktl.ArgType.TypeBool] = "bool"
|
||||
_type_dict[ktl.ArgType.TypeChar] = "str"
|
||||
_type_dict[ktl.ArgType.TypeDouble] = "float"
|
||||
_type_dict[ktl.ArgType.TypeFloat] = "float"
|
||||
_type_dict[ktl.ArgType.TypeInt] = "int"
|
||||
_type_dict[ktl.ArgType.TypeLong] = "int"
|
||||
_type_dict[ktl.ArgType.TypeLongLong] = "int"
|
||||
# _type_dict[ktl.ArgType.TypeMap] = None
|
||||
# _type_dict[ktl.ArgType.TypeObject] = None
|
||||
_type_dict[ktl.ArgType.TypeSChar] = "str"
|
||||
_type_dict[ktl.ArgType.TypeShort] = "int"
|
||||
_type_dict[ktl.ArgType.TypeString] = "str"
|
||||
_type_dict[ktl.ArgType.TypeByteArray] = "bytes"
|
||||
_type_dict[ktl.ArgType.TypeUChar] = "str"
|
||||
_type_dict[ktl.ArgType.TypeUInt] = "int"
|
||||
_type_dict[ktl.ArgType.TypeULong] = "int"
|
||||
_type_dict[ktl.ArgType.TypeULongLong] = "int"
|
||||
_type_dict[ktl.ArgType.TypeUShort] = "int"
|
||||
_type_dict[ktl.ArgType.TypeVar] = "Any"
|
||||
# _type_dict[ktl.ArgType.TypeVector] = None
|
||||
_type_dict[ktl.ArgType.TypeVoid] = "None"
|
||||
_type_dict[ktl.ArgType.TypeVoidPtr] = "None"
|
||||
|
||||
|
||||
def _translate_type(
|
||||
arg_type: ktl.ArgType, within_class: ktl.Class, is_return=False
|
||||
) -> str:
|
||||
"""Translates klayout's C-type to a type in Python.
|
||||
|
||||
This function is equivalent to the `type_to_s` in `pyaModule.cc`.
|
||||
See also `type_to_s` in `layGSIHelpProvider.cc`"""
|
||||
|
||||
py_str: str = ""
|
||||
if arg_type.type() == ktl.ArgType.TypeObject:
|
||||
if within_class.module() and arg_type.cls().module() != within_class.module():
|
||||
py_str = arg_type.cls().module() + "." + qualified_name(arg_type.cls())
|
||||
else:
|
||||
py_str = qualified_name(arg_type.cls())
|
||||
elif arg_type.type() == ktl.ArgType.TypeMap:
|
||||
inner_key = _translate_type(arg_type.inner_k(), within_class, is_return)
|
||||
inner_val = _translate_type(arg_type.inner(), within_class, is_return)
|
||||
py_str = f"Dict[{inner_key}, {inner_val}]"
|
||||
elif arg_type.type() == ktl.ArgType.TypeVector:
|
||||
if is_return:
|
||||
py_str = f"List[{_translate_type(arg_type.inner(), within_class, is_return)}]"
|
||||
else:
|
||||
py_str = f"Sequence[{_translate_type(arg_type.inner(), within_class, is_return)}]"
|
||||
else:
|
||||
py_str = _type_dict[arg_type.type()]
|
||||
|
||||
if arg_type.is_iter():
|
||||
py_str = f"Iterator[{py_str}]"
|
||||
if arg_type.has_default():
|
||||
py_str = f"Optional[{py_str}] = ..."
|
||||
return py_str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Stub:
|
||||
signature: str
|
||||
name: Any
|
||||
docstring: str
|
||||
indent_docstring: bool = True
|
||||
child_stubs: List["Stub"] = field(default_factory=list)
|
||||
decorator: str = ""
|
||||
|
||||
def __eq__(self, __o: object) -> bool:
|
||||
if not isinstance(__o, Stub):
|
||||
return False
|
||||
return (
|
||||
self.signature == __o.signature
|
||||
and self.child_stubs == __o.child_stubs
|
||||
and self.decorator == __o.decorator
|
||||
)
|
||||
|
||||
def __lt__(self, other: "Stub") -> bool:
|
||||
# mypy complains if an overload with float happens before int
|
||||
self_signature = self.signature.replace(": int", "0").replace(": float", "1")
|
||||
other_signature = other.signature.replace(": int", "0").replace(": float", "1")
|
||||
|
||||
self_sortkey = self.name, len(self.signature.split(",")), self_signature
|
||||
other_sortkey = other.name, len(other.signature.split(",")), other_signature
|
||||
return self_sortkey < other_sortkey
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.format_stub(include_docstring=False))
|
||||
|
||||
def format_stub(self, include_docstring=True):
|
||||
lines = []
|
||||
lines.extend(self.decorator.splitlines())
|
||||
if self.indent_docstring: # all but properties
|
||||
if include_docstring or len(self.child_stubs) > 0:
|
||||
lines.append(self.signature + ":")
|
||||
else:
|
||||
lines.append(self.signature + ": ...")
|
||||
else:
|
||||
lines.append(self.signature)
|
||||
|
||||
stub_str = "\n".join(lines)
|
||||
|
||||
lines = []
|
||||
lines.append('r"""')
|
||||
lines.extend(self.docstring.splitlines())
|
||||
lines.append('"""')
|
||||
doc_str = "\n".join(lines)
|
||||
|
||||
# indent only if it is required (methods, not properties)
|
||||
if self.indent_docstring:
|
||||
doc_str = indent(doc_str, " " * 4)
|
||||
|
||||
if include_docstring:
|
||||
stub_str += "\n"
|
||||
stub_str += doc_str
|
||||
|
||||
for stub in self.child_stubs:
|
||||
stub_str += "\n"
|
||||
stub_str += indent(
|
||||
stub.format_stub(include_docstring=include_docstring), " " * 4
|
||||
)
|
||||
|
||||
return stub_str
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class MethodStub(Stub):
|
||||
indent_docstring: bool = True
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class PropertyStub(Stub):
|
||||
indent_docstring: bool = False
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class ClassStub(Stub):
|
||||
indent_docstring: bool = True
|
||||
|
||||
|
||||
def get_py_child_classes(c: ktl.Class):
|
||||
for c_child in c.each_child_class():
|
||||
yield c_child
|
||||
|
||||
|
||||
def get_py_methods(
|
||||
c: ktl.Class,
|
||||
) -> List[Stub]:
|
||||
|
||||
translate_arg_type = functools.partial(_translate_type, within_class=c, is_return=False)
|
||||
translate_ret_type = functools.partial(_translate_type, within_class=c, is_return=True)
|
||||
|
||||
def _get_arglist(
|
||||
m: ktl.Method, self_str: str
|
||||
) -> List[Tuple[str, Optional[ktl.ArgType]]]:
|
||||
args: List[Tuple[str, Optional[ktl.ArgType]]] = [(self_str, None)]
|
||||
for i, a in enumerate(m.each_argument()):
|
||||
argname = a.name()
|
||||
if is_reserved_word(argname):
|
||||
argname += "_"
|
||||
elif not argname:
|
||||
argname = f"arg{i}"
|
||||
args.append((argname, a))
|
||||
return args
|
||||
|
||||
def _format_args(arglist: List[Tuple[str, Optional[str]]]):
|
||||
args = []
|
||||
for argname, argtype in arglist:
|
||||
if argtype:
|
||||
args.append(f"{argname}: {argtype}")
|
||||
else:
|
||||
args.append(argname)
|
||||
return ", ".join(args)
|
||||
|
||||
def format_args(m: ktl.Method, self_str: str = "self") -> str:
|
||||
arg_list = _get_arglist(m, self_str=self_str)
|
||||
new_arglist: List[Tuple[str, Optional[str]]] = []
|
||||
for argname, a in arg_list:
|
||||
if a:
|
||||
new_arglist.append((argname, translate_arg_type(a)))
|
||||
else:
|
||||
new_arglist.append((argname, None))
|
||||
return _format_args(new_arglist)
|
||||
|
||||
# Collect all properties here
|
||||
properties: List[Stub] = list()
|
||||
|
||||
# Extract all instance properties
|
||||
for f in c.python_properties(False):
|
||||
name = f.getter().name()
|
||||
getter = None
|
||||
if len(f.getter().methods()) > 0:
|
||||
getter = f.getter().methods()[0]
|
||||
setter = None
|
||||
if len(f.setter().methods()) > 0:
|
||||
setter = f.setter().methods()[0]
|
||||
if getter and setter:
|
||||
# Full property
|
||||
ret_type = translate_ret_type(getter.ret_type())
|
||||
doc = "Getter:\n" + getter.doc() + "\nSetter:\n" + setter.doc()
|
||||
properties.append(
|
||||
PropertyStub(
|
||||
decorator="",
|
||||
signature=f"{name}: {ret_type}",
|
||||
name=name,
|
||||
docstring=doc,
|
||||
)
|
||||
)
|
||||
elif getter:
|
||||
# Only getter
|
||||
ret_type = translate_ret_type(getter.ret_type())
|
||||
doc = getter.doc()
|
||||
properties.append(
|
||||
MethodStub(
|
||||
decorator="@property",
|
||||
signature=f"def {name}(self) -> {ret_type}",
|
||||
name=name,
|
||||
docstring=doc,
|
||||
)
|
||||
)
|
||||
elif setter:
|
||||
# Only setter
|
||||
doc = "WARNING: This variable can only be set, not retrieved.\n" + setter.doc()
|
||||
properties.append(
|
||||
MethodStub(
|
||||
decorator="@property",
|
||||
signature=f"def {name}(self) -> None",
|
||||
name=name,
|
||||
docstring=doc,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract all class properties (TODO: setters not supported currently)
|
||||
for f in c.python_properties(True):
|
||||
name = f.getter().name()
|
||||
if len(f.getter().methods()) > 0:
|
||||
getter = f.getter().methods()[0]
|
||||
ret_type = translate_ret_type(getter.ret_type())
|
||||
doc = getter.doc()
|
||||
properties.append(
|
||||
PropertyStub(
|
||||
decorator="",
|
||||
signature=f"{name}: ClassVar[{ret_type}]",
|
||||
name=name,
|
||||
docstring=doc,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract all classmethods
|
||||
classmethods: List[Stub] = list()
|
||||
|
||||
for f in c.python_methods(True):
|
||||
|
||||
name = f.name()
|
||||
|
||||
decorator = ""
|
||||
if len(f.methods()) > 1:
|
||||
decorator = "@overload\n"
|
||||
decorator += "@classmethod"
|
||||
|
||||
for m in f.methods():
|
||||
ret_type = translate_ret_type(m.ret_type())
|
||||
classmethods.append(
|
||||
MethodStub(
|
||||
decorator=decorator,
|
||||
signature=f"def {name}({format_args(m, 'cls')}) -> {ret_type}",
|
||||
name=name,
|
||||
docstring=m.doc(),
|
||||
)
|
||||
)
|
||||
|
||||
# Extract bound methods
|
||||
boundmethods: List[Stub] = list()
|
||||
|
||||
for f in c.python_methods(False):
|
||||
|
||||
name = f.name()
|
||||
|
||||
decorator = ""
|
||||
if len(f.methods()) > 1:
|
||||
decorator = "@overload\n"
|
||||
|
||||
for m in f.methods():
|
||||
|
||||
if name == "__init__":
|
||||
ret_type = "None"
|
||||
else:
|
||||
ret_type = translate_ret_type(m.ret_type())
|
||||
|
||||
arg_list = _get_arglist(m, "self")
|
||||
# TODO: fix type errors
|
||||
# Exceptions:
|
||||
# For X.__eq__(self, a:X), treat second argument as type object instead of X
|
||||
if name in ("__eq__", "__ne__"):
|
||||
arg_list[1] = arg_list[1][0], "object"
|
||||
# X._assign(self, other:X), mypy complains if _assign is defined at base class.
|
||||
# We can't specialize other in this case.
|
||||
elif name in ("_assign", "assign"):
|
||||
assert arg_list[1][1] is not None
|
||||
assert arg_list[1][1].type() == ktl.ArgType.TypeObject
|
||||
arg_list[1] = arg_list[1][0], superclass(arg_list[1][1].cls())
|
||||
else:
|
||||
new_arg_list = []
|
||||
for argname, a in arg_list:
|
||||
if a:
|
||||
new_arg_list.append((argname, translate_arg_type(a)))
|
||||
else:
|
||||
new_arg_list.append((argname, a))
|
||||
arg_list = new_arg_list
|
||||
formatted_args = _format_args(arg_list)
|
||||
|
||||
boundmethods.append(
|
||||
MethodStub(
|
||||
decorator=decorator,
|
||||
signature=f"def {name}({formatted_args}) -> {ret_type}",
|
||||
name=name,
|
||||
docstring=m.doc(),
|
||||
)
|
||||
)
|
||||
|
||||
boundmethods = sorted(boundmethods)
|
||||
properties = sorted(properties)
|
||||
classmethods = sorted(classmethods)
|
||||
|
||||
return_list: List[Stub] = properties + classmethods + boundmethods
|
||||
|
||||
return return_list
|
||||
|
||||
|
||||
def get_class_stub(
|
||||
c: ktl.Class,
|
||||
ignore: List[ktl.Class] = None,
|
||||
module: str = "",
|
||||
) -> ClassStub:
|
||||
base = ""
|
||||
if c.base():
|
||||
base = f"({c.base().name()})"
|
||||
if c.module() != module:
|
||||
full_name = c.module() + "." + c.name()
|
||||
else:
|
||||
full_name = c.name()
|
||||
_cstub = ClassStub(
|
||||
signature="class " + full_name + base, docstring=c.doc(), name=full_name
|
||||
)
|
||||
child_attributes = get_py_methods(c)
|
||||
for child_c in c.each_child_class():
|
||||
_cstub.child_stubs.append(
|
||||
get_class_stub(
|
||||
child_c,
|
||||
ignore=ignore,
|
||||
module=c.module(),
|
||||
)
|
||||
)
|
||||
for stub in child_attributes:
|
||||
_cstub.child_stubs.append(stub)
|
||||
return _cstub
|
||||
|
||||
|
||||
def get_classes(module: str) -> List[ktl.Class]:
|
||||
_classes = []
|
||||
for c in ktl.Class.each_class():
|
||||
if c.module() != module:
|
||||
continue
|
||||
_classes.append(c)
|
||||
return _classes
|
||||
|
||||
|
||||
def get_module_stubs(module: str) -> List[ClassStub]:
|
||||
_stubs = []
|
||||
_classes = get_classes(module)
|
||||
for c in _classes:
|
||||
_cstub = get_class_stub(c, ignore=_classes, module=module)
|
||||
_stubs.append(_cstub)
|
||||
return _stubs
|
||||
|
||||
|
||||
def print_mod(module, dependencies):
|
||||
print("from typing import Any, ClassVar, Dict, Sequence, List, Iterator, Optional")
|
||||
print("from typing import overload")
|
||||
for dep in dependencies:
|
||||
print(f"import klayout.{dep} as {dep}")
|
||||
for stub in get_module_stubs(module):
|
||||
print(stub.format_stub(include_docstring=True) + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) < 2:
|
||||
print("Specity module in argument")
|
||||
exit(1)
|
||||
if len(argv) == 2:
|
||||
print_mod(argv[1], [])
|
||||
else:
|
||||
print_mod(argv[1], argv[2].split(","))
|
||||
634
setup.py
634
setup.py
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
"""
|
||||
|
||||
KLayout standalone Python module setup script
|
||||
|
|
@ -55,6 +54,7 @@ and won't find them. So we need to take away the path with
|
|||
"-Wl,-soname" on Linux (see Config.link_args).
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
from setuptools import setup, Distribution, find_packages
|
||||
from setuptools.extension import Extension, Library
|
||||
import glob
|
||||
|
|
@ -70,17 +70,28 @@ import multiprocessing
|
|||
# for Jenkins we do not want to be greedy
|
||||
multicore = os.getenv("KLAYOUT_SETUP_MULTICORE")
|
||||
if multicore:
|
||||
N_cores = int(multicore)
|
||||
N_cores = int(multicore)
|
||||
else:
|
||||
N_cores = multiprocessing.cpu_count()
|
||||
N_cores = multiprocessing.cpu_count()
|
||||
|
||||
|
||||
# monkey-patch for parallel compilation
|
||||
# from https://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils
|
||||
def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None):
|
||||
def parallelCCompile(
|
||||
self,
|
||||
sources,
|
||||
output_dir=None,
|
||||
macros=None,
|
||||
include_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None,
|
||||
depends=None,
|
||||
):
|
||||
# those lines are copied from distutils.ccompiler.CCompiler directly
|
||||
macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
|
||||
output_dir, macros, include_dirs, sources, depends, extra_postargs)
|
||||
output_dir, macros, include_dirs, sources, depends, extra_postargs
|
||||
)
|
||||
cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
|
||||
# parallel code
|
||||
|
||||
|
|
@ -104,6 +115,7 @@ def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=N
|
|||
print("Building", obj, "has failed. Trying again.")
|
||||
else:
|
||||
break
|
||||
|
||||
# convert to list, imap is evaluated on-demand
|
||||
list(multiprocessing.pool.ThreadPool(N).map(_single_compile, objects))
|
||||
return objects
|
||||
|
|
@ -112,6 +124,7 @@ def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=N
|
|||
# only if python version > 2.6, somehow the travis compiler hangs in 2.6
|
||||
if sys.version_info[0] * 100 + sys.version_info[1] > 206:
|
||||
import distutils.ccompiler
|
||||
|
||||
distutils.ccompiler.CCompiler.compile = parallelCCompile
|
||||
|
||||
|
||||
|
|
@ -119,7 +132,7 @@ if sys.version_info[0] * 100 + sys.version_info[1] > 206:
|
|||
def quote_path(path):
|
||||
# looks like disutils don't need path quoting in version >= 3.9:
|
||||
if " " in path and sys.version_info[0] * 100 + sys.version_info[1] < 309:
|
||||
return "\"" + path + "\""
|
||||
return '"' + path + '"'
|
||||
else:
|
||||
return path
|
||||
|
||||
|
|
@ -127,6 +140,7 @@ def quote_path(path):
|
|||
# TODO: delete (Obsolete)
|
||||
# patch get_ext_filename
|
||||
from distutils.command.build_ext import build_ext
|
||||
|
||||
_old_get_ext_filename = build_ext.get_ext_filename
|
||||
|
||||
|
||||
|
|
@ -137,8 +151,8 @@ def patched_get_ext_filename(self, ext_name):
|
|||
"""
|
||||
filename = _old_get_ext_filename(self, ext_name)
|
||||
# Making sure this matches qmake's default extension .dylib, instead of .so
|
||||
if platform.system() == "Darwin" and '_dbpi' in ext_name:
|
||||
filename = filename.replace('.so', '.dylib')
|
||||
if platform.system() == "Darwin" and "_dbpi" in ext_name:
|
||||
filename = filename.replace(".so", ".dylib")
|
||||
return filename
|
||||
|
||||
|
||||
|
|
@ -152,15 +166,18 @@ distutils.command.build_ext.build_ext.get_ext_filename = patched_get_ext_filenam
|
|||
# hence the patch
|
||||
|
||||
from distutils.ccompiler import CCompiler
|
||||
|
||||
old_library_filename = CCompiler.library_filename
|
||||
|
||||
|
||||
def patched_library_filename(self, libname, lib_type='static', # or 'shared'
|
||||
strip_dir=0, output_dir=''):
|
||||
if platform.system() == "Darwin" and '_dbpi' in libname:
|
||||
lib_type = 'dylib'
|
||||
return old_library_filename(self, libname, lib_type=lib_type,
|
||||
strip_dir=strip_dir, output_dir=output_dir)
|
||||
def patched_library_filename(
|
||||
self, libname, lib_type="static", strip_dir=0, output_dir="" # or 'shared'
|
||||
):
|
||||
if platform.system() == "Darwin" and "_dbpi" in libname:
|
||||
lib_type = "dylib"
|
||||
return old_library_filename(
|
||||
self, libname, lib_type=lib_type, strip_dir=strip_dir, output_dir=output_dir
|
||||
)
|
||||
|
||||
|
||||
distutils.ccompiler.CCompiler.library_filename = patched_library_filename
|
||||
|
|
@ -172,16 +189,36 @@ distutils.ccompiler.CCompiler.library_filename = patched_library_filename
|
|||
# link a shared object on Linux, but a static library and patches distutils
|
||||
# for this ... We're patching this back now.
|
||||
|
||||
|
||||
def always_link_shared_object(
|
||||
self, objects, output_libname, output_dir=None, libraries=None,
|
||||
library_dirs=None, runtime_library_dirs=None, export_symbols=None,
|
||||
debug=0, extra_preargs=None, extra_postargs=None, build_temp=None,
|
||||
target_lang=None):
|
||||
self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
runtime_library_dirs=None,
|
||||
export_symbols=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None,
|
||||
build_temp=None,
|
||||
target_lang=None,
|
||||
):
|
||||
self.link(
|
||||
self.SHARED_LIBRARY, objects, output_libname,
|
||||
output_dir, libraries, library_dirs, runtime_library_dirs,
|
||||
export_symbols, debug, extra_preargs, extra_postargs,
|
||||
build_temp, target_lang
|
||||
self.SHARED_LIBRARY,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir,
|
||||
libraries,
|
||||
library_dirs,
|
||||
runtime_library_dirs,
|
||||
export_symbols,
|
||||
debug,
|
||||
extra_preargs,
|
||||
extra_postargs,
|
||||
build_temp,
|
||||
target_lang,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -201,7 +238,7 @@ class Config(object):
|
|||
|
||||
# TODO: is this really how we get the build paths?
|
||||
|
||||
build_cmd = Distribution().get_command_obj('build')
|
||||
build_cmd = Distribution().get_command_obj("build")
|
||||
build_cmd.finalize_options()
|
||||
self.build_platlib = build_cmd.build_platlib
|
||||
self.build_temp = build_cmd.build_temp
|
||||
|
|
@ -212,7 +249,7 @@ class Config(object):
|
|||
else:
|
||||
self.build_temp = os.path.join(self.build_temp, "Release")
|
||||
|
||||
build_ext_cmd = Distribution().get_command_obj('build_ext')
|
||||
build_ext_cmd = Distribution().get_command_obj("build_ext")
|
||||
|
||||
build_ext_cmd.initialize_options()
|
||||
build_ext_cmd.setup_shlib_compiler()
|
||||
|
|
@ -230,7 +267,7 @@ class Config(object):
|
|||
This code was extracted from the source in setuptools.command.build_ext.build_ext
|
||||
"""
|
||||
libtype = setuptools.command.build_ext.libtype
|
||||
full_mod = self.root + '.' + mod
|
||||
full_mod = self.root + "." + mod
|
||||
if is_lib is True:
|
||||
# Here we are guessing it is a library and that the filename
|
||||
# is to be computed by shlib_compiler.
|
||||
|
|
@ -246,8 +283,8 @@ class Config(object):
|
|||
ext_filename = os.path.basename(ext_path)
|
||||
|
||||
# Exception for database plugins, which will always be dylib.
|
||||
if platform.system() == "Darwin" and '_dbpi' in mod:
|
||||
ext_filename = ext_filename.replace('.so', '.dylib')
|
||||
if platform.system() == "Darwin" and "_dbpi" in mod:
|
||||
ext_filename = ext_filename.replace(".so", ".dylib")
|
||||
return ext_filename
|
||||
|
||||
def path_of(self, mod, mod_src_path):
|
||||
|
|
@ -265,20 +302,25 @@ class Config(object):
|
|||
"""
|
||||
Gets additional compiler arguments
|
||||
"""
|
||||
args: List[str]
|
||||
if platform.system() == "Windows":
|
||||
bits = os.getenv("KLAYOUT_BITS")
|
||||
if bits:
|
||||
return [quote_path("-I" + os.path.join(bits, "zlib", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "ptw", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "png", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "expat", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "curl", "include"))]
|
||||
args = [
|
||||
quote_path("-I" + os.path.join(bits, "zlib", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "ptw", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "png", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "expat", "include")),
|
||||
quote_path("-I" + os.path.join(bits, "curl", "include")),
|
||||
]
|
||||
else:
|
||||
return []
|
||||
args = []
|
||||
else:
|
||||
return ["-Wno-strict-aliasing", # Avoids many "type-punned pointer" warnings
|
||||
"-std=c++11", # because we use unordered_map/unordered_set
|
||||
]
|
||||
args = [
|
||||
"-Wno-strict-aliasing", # Avoids many "type-punned pointer" warnings
|
||||
"-std=c++11", # because we use unordered_map/unordered_set
|
||||
]
|
||||
return args
|
||||
|
||||
def libraries(self, mod):
|
||||
"""
|
||||
|
|
@ -286,10 +328,10 @@ class Config(object):
|
|||
"""
|
||||
if platform.system() == "Windows":
|
||||
if mod == "_tl":
|
||||
return [ "libcurl", "expat", "pthreadVCE2", "zlib", "wsock32", "libpng16" ]
|
||||
return ["libcurl", "expat", "pthreadVCE2", "zlib", "wsock32", "libpng16"]
|
||||
else:
|
||||
if mod == "_tl":
|
||||
return [ "curl", "expat", "png" ]
|
||||
return ["curl", "expat", "png"]
|
||||
return []
|
||||
|
||||
def link_args(self, mod):
|
||||
|
|
@ -300,19 +342,24 @@ class Config(object):
|
|||
args = ["/DLL"]
|
||||
bits = os.getenv("KLAYOUT_BITS")
|
||||
if bits:
|
||||
args += [quote_path("/LIBPATH:" + os.path.join(bits, "zlib", "lib")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "ptw", "libraries")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "png", "libraries")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "expat", "libraries")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "curl", "libraries"))]
|
||||
args += [
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "zlib", "libraries")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "ptw", "libraries")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "png", "libraries")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "expat", "libraries")),
|
||||
quote_path("/LIBPATH:" + os.path.join(bits, "curl", "libraries")),
|
||||
]
|
||||
return args
|
||||
elif platform.system() == "Darwin":
|
||||
# For the dependency modules, make sure we produce a dylib.
|
||||
# We can only link against such, but the bundles produced otherwise.
|
||||
args = []
|
||||
if mod[0] == "_":
|
||||
args += ["-Wl,-dylib", '-Wl,-install_name,@rpath/%s' % self.libname_of(mod, is_lib=True)]
|
||||
args += ['-Wl,-rpath,@loader_path/']
|
||||
args += [
|
||||
"-Wl,-dylib",
|
||||
"-Wl,-install_name,@rpath/%s" % self.libname_of(mod, is_lib=True),
|
||||
]
|
||||
args += ["-Wl,-rpath,@loader_path/"]
|
||||
return args
|
||||
else:
|
||||
# this makes the libraries suitable for linking with a path -
|
||||
|
|
@ -321,23 +368,30 @@ class Config(object):
|
|||
# will look for the path-qualified library. But that's the
|
||||
# build path and the loader will fail.
|
||||
args = []
|
||||
args += ['-Wl,-soname,' + self.libname_of(mod, is_lib=True)]
|
||||
if '_dbpi' not in mod:
|
||||
loader_path = '$ORIGIN'
|
||||
args += ["-Wl,-soname," + self.libname_of(mod, is_lib=True)]
|
||||
if "_dbpi" not in mod:
|
||||
loader_path = "$ORIGIN"
|
||||
else:
|
||||
loader_path = '$ORIGIN/..'
|
||||
args += ['-Wl,-rpath,' + loader_path]
|
||||
loader_path = "$ORIGIN/.."
|
||||
args += ["-Wl,-rpath," + loader_path]
|
||||
# default linux shared object compilation uses the '-g' flag,
|
||||
# which generates unnecessary debug information
|
||||
# removing with strip-all during the linking stage
|
||||
args += ['-Wl,--strip-all']
|
||||
args += ["-Wl,--strip-all"]
|
||||
return args
|
||||
|
||||
def macros(self):
|
||||
"""
|
||||
Returns the macros to use for building
|
||||
"""
|
||||
return [('HAVE_PNG', 1), ('HAVE_CURL', 1), ('HAVE_EXPAT', 1), ('KLAYOUT_MAJOR_VERSION', self.major_version()), ('KLAYOUT_MINOR_VERSION', self.minor_version())]
|
||||
return [
|
||||
("HAVE_PNG", 1),
|
||||
("HAVE_CURL", 1),
|
||||
("HAVE_EXPAT", 1),
|
||||
("KLAYOUT_MAJOR_VERSION", self.major_version()),
|
||||
("KLAYOUT_MINOR_VERSION", self.minor_version()),
|
||||
("GSI_ALIAS_INSPECT", 1),
|
||||
]
|
||||
|
||||
def minor_version(self):
|
||||
"""
|
||||
|
|
@ -349,7 +403,9 @@ class Config(object):
|
|||
version_file = os.path.join(os.path.dirname(__file__), "version.sh")
|
||||
with open(version_file, "r") as file:
|
||||
version_txt = file.read()
|
||||
rm = re.search(r"KLAYOUT_VERSION\s*=\s*\"(.*?)\.(.*?)(\..*)?\".*", version_txt)
|
||||
rm = re.search(
|
||||
r"KLAYOUT_VERSION\s*=\s*\"(.*?)\.(.*?)(\..*)?\".*", version_txt
|
||||
)
|
||||
if rm:
|
||||
version_string = rm.group(2)
|
||||
return version_string
|
||||
|
|
@ -366,7 +422,9 @@ class Config(object):
|
|||
version_file = os.path.join(os.path.dirname(__file__), "version.sh")
|
||||
with open(version_file, "r") as file:
|
||||
version_txt = file.read()
|
||||
rm = re.search(r"KLAYOUT_VERSION\s*=\s*\"(.*?)\.(.*?)(\..*)?\".*", version_txt)
|
||||
rm = re.search(
|
||||
r"KLAYOUT_VERSION\s*=\s*\"(.*?)\.(.*?)(\..*)?\".*", version_txt
|
||||
)
|
||||
if rm:
|
||||
version_string = rm.group(1)
|
||||
return version_string
|
||||
|
|
@ -407,13 +465,15 @@ _tl_sources.discard(os.path.join(_tl_path, "tlHttpStreamNoQt.cc"))
|
|||
_tl_sources.discard(os.path.join(_tl_path, "tlFileSystemWatcher.cc"))
|
||||
_tl_sources.discard(os.path.join(_tl_path, "tlDeferredExecutionQt.cc"))
|
||||
|
||||
_tl = Library(config.root + '._tl',
|
||||
define_macros=config.macros() + [('MAKE_TL_LIBRARY', 1)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_tl'),
|
||||
extra_link_args=config.link_args('_tl'),
|
||||
extra_compile_args=config.compile_args('_tl'),
|
||||
sources=list(_tl_sources))
|
||||
_tl = Library(
|
||||
config.root + "._tl",
|
||||
define_macros=config.macros() + [("MAKE_TL_LIBRARY", 1)],
|
||||
language="c++",
|
||||
libraries=config.libraries("_tl"),
|
||||
extra_link_args=config.link_args("_tl"),
|
||||
extra_compile_args=config.compile_args("_tl"),
|
||||
sources=list(_tl_sources),
|
||||
)
|
||||
|
||||
config.add_extension(_tl)
|
||||
|
||||
|
|
@ -423,15 +483,17 @@ config.add_extension(_tl)
|
|||
_gsi_path = os.path.join("src", "gsi", "gsi")
|
||||
_gsi_sources = set(glob.glob(os.path.join(_gsi_path, "*.cc")))
|
||||
|
||||
_gsi = Library(config.root + '._gsi',
|
||||
define_macros=config.macros() + [('MAKE_GSI_LIBRARY', 1)],
|
||||
include_dirs=[_tl_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_gsi'),
|
||||
extra_link_args=config.link_args('_gsi'),
|
||||
extra_compile_args=config.compile_args('_gsi'),
|
||||
sources=list(_gsi_sources))
|
||||
_gsi = Library(
|
||||
config.root + "._gsi",
|
||||
define_macros=config.macros() + [("MAKE_GSI_LIBRARY", 1)],
|
||||
include_dirs=[_tl_path],
|
||||
extra_objects=[config.path_of("_tl", _tl_path)],
|
||||
language="c++",
|
||||
libraries=config.libraries('_gsi'),
|
||||
extra_link_args=config.link_args("_gsi"),
|
||||
extra_compile_args=config.compile_args("_gsi"),
|
||||
sources=list(_gsi_sources),
|
||||
)
|
||||
config.add_extension(_gsi)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -440,15 +502,17 @@ config.add_extension(_gsi)
|
|||
_pya_path = os.path.join("src", "pya", "pya")
|
||||
_pya_sources = set(glob.glob(os.path.join(_pya_path, "*.cc")))
|
||||
|
||||
_pya = Library(config.root + '._pya',
|
||||
define_macros=config.macros() + [('MAKE_PYA_LIBRARY', 1)],
|
||||
include_dirs=[_tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_pya'),
|
||||
extra_link_args=config.link_args('_pya'),
|
||||
extra_compile_args=config.compile_args('_pya'),
|
||||
sources=list(_pya_sources))
|
||||
_pya = Library(
|
||||
config.root + "._pya",
|
||||
define_macros=config.macros() + [("MAKE_PYA_LIBRARY", 1)],
|
||||
include_dirs=[_tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of("_tl", _tl_path), config.path_of("_gsi", _gsi_path)],
|
||||
language="c++",
|
||||
libraries=config.libraries('_pya'),
|
||||
extra_link_args=config.link_args("_pya"),
|
||||
extra_compile_args=config.compile_args("_pya"),
|
||||
sources=list(_pya_sources),
|
||||
)
|
||||
config.add_extension(_pya)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -457,15 +521,17 @@ config.add_extension(_pya)
|
|||
_rba_path = os.path.join("src", "rbastub")
|
||||
_rba_sources = set(glob.glob(os.path.join(_rba_path, "*.cc")))
|
||||
|
||||
_rba = Library(config.root + '._rba',
|
||||
define_macros=config.macros() + [('MAKE_RBA_LIBRARY', 1)],
|
||||
include_dirs=[_tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_rba'),
|
||||
extra_link_args=config.link_args('_rba'),
|
||||
extra_compile_args=config.compile_args('_rba'),
|
||||
sources=list(_rba_sources))
|
||||
_rba = Library(
|
||||
config.root + '._rba',
|
||||
define_macros=config.macros() + [('MAKE_RBA_LIBRARY', 1)],
|
||||
include_dirs=[_tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_rba'),
|
||||
extra_link_args=config.link_args('_rba'),
|
||||
extra_compile_args=config.compile_args('_rba'),
|
||||
sources=list(_rba_sources)
|
||||
)
|
||||
config.add_extension(_rba)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -474,15 +540,17 @@ config.add_extension(_rba)
|
|||
_db_path = os.path.join("src", "db", "db")
|
||||
_db_sources = set(glob.glob(os.path.join(_db_path, "*.cc")))
|
||||
|
||||
_db = Library(config.root + '._db',
|
||||
define_macros=config.macros() + [('MAKE_DB_LIBRARY', 1)],
|
||||
include_dirs=[_tl_path, _gsi_path, _db_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_db'),
|
||||
extra_link_args=config.link_args('_db'),
|
||||
extra_compile_args=config.compile_args('_db'),
|
||||
sources=list(_db_sources))
|
||||
_db = Library(
|
||||
config.root + "._db",
|
||||
define_macros=config.macros() + [("MAKE_DB_LIBRARY", 1)],
|
||||
include_dirs=[_tl_path, _gsi_path, _db_path],
|
||||
extra_objects=[config.path_of("_tl", _tl_path), config.path_of("_gsi", _gsi_path)],
|
||||
language="c++",
|
||||
libraries=config.libraries('_db'),
|
||||
extra_link_args=config.link_args("_db"),
|
||||
extra_compile_args=config.compile_args("_db"),
|
||||
sources=list(_db_sources),
|
||||
)
|
||||
config.add_extension(_db)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -491,15 +559,21 @@ config.add_extension(_db)
|
|||
_lib_path = os.path.join("src", "lib", "lib")
|
||||
_lib_sources = set(glob.glob(os.path.join(_lib_path, "*.cc")))
|
||||
|
||||
_lib = Library(config.root + '._lib',
|
||||
define_macros=config.macros() + [('MAKE_LIB_LIBRARY', 1)],
|
||||
include_dirs=[_tl_path, _gsi_path, _db_path, _lib_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_lib'),
|
||||
extra_link_args=config.link_args('_lib'),
|
||||
extra_compile_args=config.compile_args('_lib'),
|
||||
sources=list(_lib_sources))
|
||||
_lib = Library(
|
||||
config.root + "._lib",
|
||||
define_macros=config.macros() + [("MAKE_LIB_LIBRARY", 1)],
|
||||
include_dirs=[_tl_path, _gsi_path, _db_path, _lib_path],
|
||||
extra_objects=[
|
||||
config.path_of("_tl", _tl_path),
|
||||
config.path_of("_gsi", _gsi_path),
|
||||
config.path_of("_db", _db_path),
|
||||
],
|
||||
language="c++",
|
||||
libraries=config.libraries('_lib'),
|
||||
extra_link_args=config.link_args("_lib"),
|
||||
extra_compile_args=config.compile_args("_lib"),
|
||||
sources=list(_lib_sources),
|
||||
)
|
||||
config.add_extension(_lib)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -508,15 +582,21 @@ config.add_extension(_lib)
|
|||
_rdb_path = os.path.join("src", "rdb", "rdb")
|
||||
_rdb_sources = set(glob.glob(os.path.join(_rdb_path, "*.cc")))
|
||||
|
||||
_rdb = Library(config.root + '._rdb',
|
||||
define_macros=config.macros() + [('MAKE_RDB_LIBRARY', 1)],
|
||||
include_dirs=[_db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_rdb'),
|
||||
extra_link_args=config.link_args('_rdb'),
|
||||
extra_compile_args=config.compile_args('_rdb'),
|
||||
sources=list(_rdb_sources))
|
||||
_rdb = Library(
|
||||
config.root + "._rdb",
|
||||
define_macros=config.macros() + [("MAKE_RDB_LIBRARY", 1)],
|
||||
include_dirs=[_db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[
|
||||
config.path_of("_tl", _tl_path),
|
||||
config.path_of("_gsi", _gsi_path),
|
||||
config.path_of("_db", _db_path),
|
||||
],
|
||||
language="c++",
|
||||
libraries=config.libraries('_rdb'),
|
||||
extra_link_args=config.link_args("_rdb"),
|
||||
extra_compile_args=config.compile_args("_rdb"),
|
||||
sources=list(_rdb_sources),
|
||||
)
|
||||
config.add_extension(_rdb)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -525,15 +605,22 @@ config.add_extension(_rdb)
|
|||
_laybasic_path = os.path.join("src", "laybasic", "laybasic")
|
||||
_laybasic_sources = set(glob.glob(os.path.join(_laybasic_path, "*.cc")))
|
||||
|
||||
_laybasic = Library(config.root + '._laybasic',
|
||||
define_macros=config.macros() + [('MAKE_LAYBASIC_LIBRARY', 1)],
|
||||
include_dirs=[_rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_rdb', _rdb_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_laybasic'),
|
||||
extra_link_args=config.link_args('_laybasic'),
|
||||
extra_compile_args=config.compile_args('_laybasic'),
|
||||
sources=list(_laybasic_sources))
|
||||
_laybasic = Library(
|
||||
config.root + '._laybasic',
|
||||
define_macros=config.macros() + [('MAKE_LAYBASIC_LIBRARY', 1)],
|
||||
include_dirs=[_rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[
|
||||
config.path_of('_rdb', _rdb_path),
|
||||
config.path_of('_tl', _tl_path),
|
||||
config.path_of('_gsi', _gsi_path),
|
||||
config.path_of('_db', _db_path)
|
||||
],
|
||||
language='c++',
|
||||
libraries=config.libraries('_laybasic'),
|
||||
extra_link_args=config.link_args('_laybasic'),
|
||||
extra_compile_args=config.compile_args('_laybasic'),
|
||||
sources=list(_laybasic_sources)
|
||||
)
|
||||
config.add_extension(_laybasic)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -542,15 +629,23 @@ config.add_extension(_laybasic)
|
|||
_layview_path = os.path.join("src", "layview", "layview")
|
||||
_layview_sources = set(glob.glob(os.path.join(_layview_path, "*.cc")))
|
||||
|
||||
_layview = Library(config.root + '._layview',
|
||||
define_macros=config.macros() + [('MAKE_LAYVIEW_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_laybasic', _laybasic_path), config.path_of('_rdb', _rdb_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_layview'),
|
||||
extra_link_args=config.link_args('_layview'),
|
||||
extra_compile_args=config.compile_args('_layview'),
|
||||
sources=list(_layview_sources))
|
||||
_layview = Library(
|
||||
config.root + '._layview',
|
||||
define_macros=config.macros() + [('MAKE_LAYVIEW_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[
|
||||
config.path_of('_laybasic', _laybasic_path),
|
||||
config.path_of('_rdb', _rdb_path),
|
||||
config.path_of('_tl', _tl_path),
|
||||
config.path_of('_gsi', _gsi_path),
|
||||
config.path_of('_db', _db_path)
|
||||
],
|
||||
language='c++',
|
||||
libraries=config.libraries('_layview'),
|
||||
extra_link_args=config.link_args('_layview'),
|
||||
extra_compile_args=config.compile_args('_layview'),
|
||||
sources=list(_layview_sources)
|
||||
)
|
||||
config.add_extension(_layview)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -559,15 +654,22 @@ config.add_extension(_layview)
|
|||
_lym_path = os.path.join("src", "lym", "lym")
|
||||
_lym_sources = set(glob.glob(os.path.join(_lym_path, "*.cc")))
|
||||
|
||||
_lym = Library(config.root + '._lym',
|
||||
define_macros=config.macros() + [('MAKE_LYM_LIBRARY', 1)],
|
||||
include_dirs=[_pya_path, _rba_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_rba', _rba_path), config.path_of('_pya', _pya_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_lym'),
|
||||
extra_link_args=config.link_args('_lym'),
|
||||
extra_compile_args=config.compile_args('_lym'),
|
||||
sources=list(_lym_sources))
|
||||
_lym = Library(
|
||||
config.root + '._lym',
|
||||
define_macros=config.macros() + [('MAKE_LYM_LIBRARY', 1)],
|
||||
include_dirs=[_pya_path, _rba_path, _tl_path, _gsi_path],
|
||||
extra_objects=[
|
||||
config.path_of('_rba', _rba_path),
|
||||
config.path_of('_pya', _pya_path),
|
||||
config.path_of('_tl', _tl_path),
|
||||
config.path_of('_gsi', _gsi_path)
|
||||
],
|
||||
language='c++',
|
||||
libraries=config.libraries('_lym'),
|
||||
extra_link_args=config.link_args('_lym'),
|
||||
extra_compile_args=config.compile_args('_lym'),
|
||||
sources=list(_lym_sources)
|
||||
)
|
||||
config.add_extension(_lym)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -576,15 +678,24 @@ config.add_extension(_lym)
|
|||
_ant_path = os.path.join("src", "ant", "ant")
|
||||
_ant_sources = set(glob.glob(os.path.join(_ant_path, "*.cc")))
|
||||
|
||||
_ant = Library(config.root + '._ant',
|
||||
define_macros=config.macros() + [('MAKE_ANT_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _layview_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_laybasic', _laybasic_path), config.path_of('_layview', _layview_path), config.path_of('_rdb', _rdb_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_ant'),
|
||||
extra_link_args=config.link_args('_ant'),
|
||||
extra_compile_args=config.compile_args('_ant'),
|
||||
sources=list(_ant_sources))
|
||||
_ant = Library(
|
||||
config.root + '._ant',
|
||||
define_macros=config.macros() + [('MAKE_ANT_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _layview_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[
|
||||
config.path_of('_laybasic', _laybasic_path),
|
||||
config.path_of('_layview', _layview_path),
|
||||
config.path_of('_rdb', _rdb_path),
|
||||
config.path_of('_tl', _tl_path),
|
||||
config.path_of('_gsi', _gsi_path),
|
||||
config.path_of('_db', _db_path)
|
||||
],
|
||||
language='c++',
|
||||
libraries=config.libraries('_ant'),
|
||||
extra_link_args=config.link_args('_ant'),
|
||||
extra_compile_args=config.compile_args('_ant'),
|
||||
sources=list(_ant_sources)
|
||||
)
|
||||
config.add_extension(_ant)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -593,15 +704,24 @@ config.add_extension(_ant)
|
|||
_img_path = os.path.join("src", "img", "img")
|
||||
_img_sources = set(glob.glob(os.path.join(_img_path, "*.cc")))
|
||||
|
||||
_img = Library(config.root + '._img',
|
||||
define_macros=config.macros() + [('MAKE_IMG_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _layview_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_laybasic', _laybasic_path), config.path_of('_layview', _layview_path), config.path_of('_rdb', _rdb_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_img'),
|
||||
extra_link_args=config.link_args('_img'),
|
||||
extra_compile_args=config.compile_args('_img'),
|
||||
sources=list(_img_sources))
|
||||
_img = Library(
|
||||
config.root + '._img',
|
||||
define_macros=config.macros() + [('MAKE_IMG_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _layview_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[
|
||||
config.path_of('_laybasic', _laybasic_path),
|
||||
config.path_of('_layview', _layview_path),
|
||||
config.path_of('_rdb', _rdb_path),
|
||||
config.path_of('_tl', _tl_path),
|
||||
config.path_of('_gsi', _gsi_path),
|
||||
config.path_of('_db', _db_path)
|
||||
],
|
||||
language='c++',
|
||||
libraries=config.libraries('_img'),
|
||||
extra_link_args=config.link_args('_img'),
|
||||
extra_compile_args=config.compile_args('_img'),
|
||||
sources=list(_img_sources)
|
||||
)
|
||||
config.add_extension(_img)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -610,15 +730,24 @@ config.add_extension(_img)
|
|||
_edt_path = os.path.join("src", "edt", "edt")
|
||||
_edt_sources = set(glob.glob(os.path.join(_edt_path, "*.cc")))
|
||||
|
||||
_edt = Library(config.root + '._edt',
|
||||
define_macros=config.macros() + [('MAKE_EDT_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _layview_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_laybasic', _laybasic_path), config.path_of('_layview', _layview_path), config.path_of('_rdb', _rdb_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
libraries=config.libraries('_edt'),
|
||||
extra_link_args=config.link_args('_edt'),
|
||||
extra_compile_args=config.compile_args('_edt'),
|
||||
sources=list(_edt_sources))
|
||||
_edt = Library(
|
||||
config.root + '._edt',
|
||||
define_macros=config.macros() + [('MAKE_EDT_LIBRARY', 1)],
|
||||
include_dirs=[_laybasic_path, _layview_path, _rdb_path, _db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[
|
||||
config.path_of('_laybasic', _laybasic_path),
|
||||
config.path_of('_layview', _layview_path),
|
||||
config.path_of('_rdb', _rdb_path),
|
||||
config.path_of('_tl', _tl_path),
|
||||
config.path_of('_gsi', _gsi_path),
|
||||
config.path_of('_db', _db_path)
|
||||
],
|
||||
language='c++',
|
||||
libraries=config.libraries('_edt'),
|
||||
extra_link_args=config.link_args('_edt'),
|
||||
extra_compile_args=config.compile_args('_edt'),
|
||||
sources=list(_edt_sources)
|
||||
)
|
||||
config.add_extension(_edt)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -635,15 +764,25 @@ for pi in dbpi_dirs:
|
|||
|
||||
pi_sources = glob.glob(os.path.join(pi, "*.cc"))
|
||||
|
||||
pi_ext = Library(config.root + '.db_plugins.' + mod_name,
|
||||
define_macros=config.macros() + [('MAKE_DB_PLUGIN_LIBRARY', 1)],
|
||||
include_dirs=[os.path.join("src", "plugins", "common"),
|
||||
_db_path, _tl_path, _gsi_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)],
|
||||
language='c++',
|
||||
extra_link_args=config.link_args(mod_name),
|
||||
extra_compile_args=config.compile_args(mod_name),
|
||||
sources=pi_sources)
|
||||
pi_ext = Library(
|
||||
config.root + ".db_plugins." + mod_name,
|
||||
define_macros=config.macros() + [("MAKE_DB_PLUGIN_LIBRARY", 1)],
|
||||
include_dirs=[
|
||||
os.path.join("src", "plugins", "common"),
|
||||
_db_path,
|
||||
_tl_path,
|
||||
_gsi_path,
|
||||
],
|
||||
extra_objects=[
|
||||
config.path_of("_tl", _tl_path),
|
||||
config.path_of("_gsi", _gsi_path),
|
||||
config.path_of("_db", _db_path),
|
||||
],
|
||||
language="c++",
|
||||
extra_link_args=config.link_args(mod_name),
|
||||
extra_compile_args=config.compile_args(mod_name),
|
||||
sources=pi_sources,
|
||||
)
|
||||
|
||||
db_plugins.append(pi_ext)
|
||||
config.add_extension(pi_ext)
|
||||
|
|
@ -654,13 +793,19 @@ for pi in dbpi_dirs:
|
|||
tl_path = os.path.join("src", "pymod", "tl")
|
||||
tl_sources = set(glob.glob(os.path.join(tl_path, "*.cc")))
|
||||
|
||||
tl = Extension(config.root + '.tlcore',
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_pya', _pya_path)],
|
||||
extra_link_args=config.link_args('tlcore'),
|
||||
extra_compile_args=config.compile_args('tlcore'),
|
||||
sources=list(tl_sources))
|
||||
tl = Extension(
|
||||
config.root + ".tlcore",
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[
|
||||
config.path_of("_tl", _tl_path),
|
||||
config.path_of("_gsi", _gsi_path),
|
||||
config.path_of("_pya", _pya_path),
|
||||
],
|
||||
extra_link_args=config.link_args("tlcore"),
|
||||
extra_compile_args=config.compile_args("tlcore"),
|
||||
sources=list(tl_sources),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# db extension library
|
||||
|
|
@ -668,13 +813,20 @@ tl = Extension(config.root + '.tlcore',
|
|||
db_path = os.path.join("src", "pymod", "db")
|
||||
db_sources = set(glob.glob(os.path.join(db_path, "*.cc")))
|
||||
|
||||
db = Extension(config.root + '.dbcore',
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_db_path, _tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[config.path_of('_db', _db_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_pya', _pya_path)],
|
||||
extra_link_args=config.link_args('dbcore'),
|
||||
extra_compile_args=config.compile_args('dbcore'),
|
||||
sources=list(db_sources))
|
||||
db = Extension(
|
||||
config.root + ".dbcore",
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_db_path, _tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[
|
||||
config.path_of("_db", _db_path),
|
||||
config.path_of("_tl", _tl_path),
|
||||
config.path_of("_gsi", _gsi_path),
|
||||
config.path_of("_pya", _pya_path),
|
||||
],
|
||||
extra_link_args=config.link_args("dbcore"),
|
||||
extra_compile_args=config.compile_args("dbcore"),
|
||||
sources=list(db_sources),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# lib extension library
|
||||
|
|
@ -682,13 +834,20 @@ db = Extension(config.root + '.dbcore',
|
|||
lib_path = os.path.join("src", "pymod", "lib")
|
||||
lib_sources = set(glob.glob(os.path.join(lib_path, "*.cc")))
|
||||
|
||||
lib = Extension(config.root + '.libcore',
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_lib_path, _tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[config.path_of('_lib', _lib_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_pya', _pya_path)],
|
||||
extra_link_args=config.link_args('libcore'),
|
||||
extra_compile_args=config.compile_args('libcore'),
|
||||
sources=list(lib_sources))
|
||||
lib = Extension(
|
||||
config.root + ".libcore",
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_lib_path, _tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[
|
||||
config.path_of("_lib", _lib_path),
|
||||
config.path_of("_tl", _tl_path),
|
||||
config.path_of("_gsi", _gsi_path),
|
||||
config.path_of("_pya", _pya_path),
|
||||
],
|
||||
extra_link_args=config.link_args("libcore"),
|
||||
extra_compile_args=config.compile_args("libcore"),
|
||||
sources=list(lib_sources),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# rdb extension library
|
||||
|
|
@ -696,13 +855,20 @@ lib = Extension(config.root + '.libcore',
|
|||
rdb_path = os.path.join("src", "pymod", "rdb")
|
||||
rdb_sources = set(glob.glob(os.path.join(rdb_path, "*.cc")))
|
||||
|
||||
rdb = Extension(config.root + '.rdbcore',
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_rdb_path, _tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[config.path_of('_rdb', _rdb_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_pya', _pya_path)],
|
||||
extra_link_args=config.link_args('rdbcore'),
|
||||
extra_compile_args=config.compile_args('rdbcore'),
|
||||
sources=list(rdb_sources))
|
||||
rdb = Extension(
|
||||
config.root + ".rdbcore",
|
||||
define_macros=config.macros(),
|
||||
include_dirs=[_rdb_path, _tl_path, _gsi_path, _pya_path],
|
||||
extra_objects=[
|
||||
config.path_of("_rdb", _rdb_path),
|
||||
config.path_of("_tl", _tl_path),
|
||||
config.path_of("_gsi", _gsi_path),
|
||||
config.path_of("_pya", _pya_path),
|
||||
],
|
||||
extra_link_args=config.link_args("rdbcore"),
|
||||
extra_compile_args=config.compile_args("rdbcore"),
|
||||
sources=list(rdb_sources),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# lay extension library
|
||||
|
|
@ -737,28 +903,34 @@ lay = Extension(config.root + '.laycore',
|
|||
# ------------------------------------------------------------------
|
||||
# Core setup function
|
||||
|
||||
if __name__ == '__main__':
|
||||
setup(name=config.root,
|
||||
version=config.version(),
|
||||
license='GNU GPLv3',
|
||||
description='KLayout standalone Python package',
|
||||
long_description='This package is a standalone distribution of KLayout\'s Python API.\n\nFor more details see here: https://www.klayout.org/klayout-pypi',
|
||||
author='Matthias Koefferlein',
|
||||
author_email='matthias@klayout.de',
|
||||
classifiers=[
|
||||
# Recommended classifiers
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
# Optional classifiers
|
||||
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
||||
],
|
||||
url='https://github.com/klayout/klayout',
|
||||
packages=find_packages('src/pymod/distutils_src'),
|
||||
package_dir={'': 'src/pymod/distutils_src'}, # https://github.com/pypa/setuptools/issues/230
|
||||
package_data={config.root: ["src/pymod/distutils_src/klayout/*.pyi"]},
|
||||
include_package_data=True,
|
||||
ext_modules=[_tl, _gsi, _pya, _rba, _db, _lib, _rdb, _lym, _laybasic, _layview, _ant, _edt, _img] + db_plugins + [tl, db, lib, rdb, lay])
|
||||
if __name__ == "__main__":
|
||||
setup(
|
||||
name=config.root,
|
||||
version=config.version(),
|
||||
license="GNU GPLv3",
|
||||
description="KLayout standalone Python package",
|
||||
long_description="This package is a standalone distribution of KLayout's Python API.\n\nFor more details see here: https://www.klayout.org/klayout-pypi",
|
||||
author="Matthias Koefferlein",
|
||||
author_email="matthias@klayout.de",
|
||||
classifiers=[
|
||||
# Recommended classifiers
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
# Optional classifiers
|
||||
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
||||
],
|
||||
url="https://github.com/klayout/klayout",
|
||||
packages=find_packages("src/pymod/distutils_src"),
|
||||
package_dir={
|
||||
"": "src/pymod/distutils_src"
|
||||
}, # https://github.com/pypa/setuptools/issues/230
|
||||
package_data={config.root: ["src/pymod/distutils_src/klayout/*.pyi"]},
|
||||
include_package_data=True,
|
||||
ext_modules=[_tl, _gsi, _pya, _rba, _db, _lib, _rdb, _lym, _laybasic, _layview, _ant, _edt, _img]
|
||||
+ db_plugins
|
||||
+ [tl, db, lib, rdb, lay]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace gsi
|
|||
// db::Library binding
|
||||
|
||||
/**
|
||||
* @brief A basic implementation of the library
|
||||
* @brief A basic implementation of the library
|
||||
*/
|
||||
static db::Library *new_lib ()
|
||||
{
|
||||
|
|
@ -173,15 +173,15 @@ LibraryClass decl_Library ("db", "Library",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25.\n"
|
||||
) +
|
||||
gsi::method ("name", &db::Library::get_name,
|
||||
gsi::method ("name", &db::Library::get_name,
|
||||
"@brief Returns the libraries' name\n"
|
||||
"The name is set when the library is registered and cannot be changed\n"
|
||||
) +
|
||||
gsi::method ("id", &db::Library::get_id,
|
||||
gsi::method ("id", &db::Library::get_id,
|
||||
"@brief Returns the library's ID\n"
|
||||
"The ID is set when the library is registered and cannot be changed \n"
|
||||
) +
|
||||
gsi::method ("description", &db::Library::get_description,
|
||||
gsi::method ("description", &db::Library::get_description,
|
||||
"@brief Returns the libraries' description text\n"
|
||||
) +
|
||||
gsi::method ("description=", &db::Library::set_description, gsi::arg ("description"),
|
||||
|
|
@ -232,7 +232,7 @@ LibraryClass decl_Library ("db", "Library",
|
|||
gsi::method ("layout_const", (const db::Layout &(db::Library::*)() const) &db::Library::layout,
|
||||
"@brief The layout object where the cells reside that this library defines (const version)\n"
|
||||
) +
|
||||
gsi::method ("layout", (db::Layout &(db::Library::*)()) &db::Library::layout,
|
||||
gsi::method ("layout", (db::Layout &(db::Library::*)()) &db::Library::layout,
|
||||
"@brief The layout object where the cells reside that this library defines\n"
|
||||
) +
|
||||
gsi::method ("refresh", &db::Library::refresh,
|
||||
|
|
@ -396,7 +396,7 @@ class PCellDeclarationImpl
|
|||
: public db::PCellDeclaration
|
||||
{
|
||||
public:
|
||||
// dummy implementation to provide the signature
|
||||
// dummy implementation to provide the signature
|
||||
virtual std::vector<db::LayerProperties> get_layer_declarations_impl (const db::pcell_parameters_type &) const
|
||||
{
|
||||
return std::vector<db::LayerProperties> ();
|
||||
|
|
@ -433,7 +433,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// dummy implementation to provide the signature
|
||||
// dummy implementation to provide the signature
|
||||
virtual db::pcell_parameters_type coerce_parameters_impl (const db::Layout & /*layout*/, const db::pcell_parameters_type &input) const
|
||||
{
|
||||
return input;
|
||||
|
|
@ -581,7 +581,7 @@ Class<PCellDeclarationImpl> decl_PCellDeclaration (decl_PCellDeclaration_Native,
|
|||
"This method receives the PCell parameters which allows it to deduce layers\n"
|
||||
"from the parameters."
|
||||
) +
|
||||
gsi::callback ("get_parameters", &PCellDeclarationImpl::get_parameter_declarations, &PCellDeclarationImpl::cb_get_parameter_declarations,
|
||||
gsi::callback ("get_parameters", &PCellDeclarationImpl::get_parameter_declarations, &PCellDeclarationImpl::cb_get_parameter_declarations,
|
||||
"@brief Returns a list of parameter declarations\n"
|
||||
"Reimplement this method to return a list of parameters used in that PCell \n"
|
||||
"implementation. A parameter declaration is a PCellParameterDeclaration object\n"
|
||||
|
|
@ -824,33 +824,33 @@ Class<db::PCellParameterDeclaration> decl_PCellParameterDeclaration ("db", "PCel
|
|||
"@param default The default (initial) value\n"
|
||||
"@param unit The unit string\n"
|
||||
) +
|
||||
gsi::method ("name", &db::PCellParameterDeclaration::get_name,
|
||||
gsi::method ("name", &db::PCellParameterDeclaration::get_name,
|
||||
"@brief Gets the name\n"
|
||||
) +
|
||||
gsi::method ("name=", &db::PCellParameterDeclaration::set_name, gsi::arg ("value"),
|
||||
"@brief Sets the name\n"
|
||||
) +
|
||||
gsi::method ("unit", &db::PCellParameterDeclaration::get_unit,
|
||||
gsi::method ("unit", &db::PCellParameterDeclaration::get_unit,
|
||||
"@brief Gets the unit string\n"
|
||||
) +
|
||||
gsi::method ("unit=", &db::PCellParameterDeclaration::set_unit, gsi::arg ("unit"),
|
||||
"@brief Sets the unit string\n"
|
||||
"The unit string is shown right to the edit fields for numeric parameters.\n"
|
||||
) +
|
||||
gsi::method_ext ("type", &get_type,
|
||||
gsi::method_ext ("type", &get_type,
|
||||
"@brief Gets the type\n"
|
||||
"The type is one of the T... constants."
|
||||
) +
|
||||
gsi::method_ext ("type=", &set_type, gsi::arg ("type"),
|
||||
"@brief Sets the type\n"
|
||||
) +
|
||||
gsi::method ("description", &db::PCellParameterDeclaration::get_description,
|
||||
gsi::method ("description", &db::PCellParameterDeclaration::get_description,
|
||||
"@brief Gets the description text\n"
|
||||
) +
|
||||
gsi::method ("description=", &db::PCellParameterDeclaration::set_description, gsi::arg ("description"),
|
||||
"@brief Sets the description\n"
|
||||
) +
|
||||
gsi::method ("hidden?", &db::PCellParameterDeclaration::is_hidden,
|
||||
gsi::method ("hidden?", &db::PCellParameterDeclaration::is_hidden,
|
||||
"@brief Returns true, if the parameter is a hidden parameter that should not be shown in the user interface\n"
|
||||
"By making a parameter hidden, it is possible to create internal parameters which cannot be\n"
|
||||
"edited.\n"
|
||||
|
|
@ -858,7 +858,7 @@ Class<db::PCellParameterDeclaration> decl_PCellParameterDeclaration ("db", "PCel
|
|||
gsi::method ("hidden=", &db::PCellParameterDeclaration::set_hidden, gsi::arg ("flag"),
|
||||
"@brief Makes the parameter hidden if this attribute is set to true\n"
|
||||
) +
|
||||
gsi::method ("readonly?", &db::PCellParameterDeclaration::is_readonly,
|
||||
gsi::method ("readonly?", &db::PCellParameterDeclaration::is_readonly,
|
||||
"@brief Returns true, if the parameter is a read-only parameter\n"
|
||||
"By making a parameter read-only, it is shown but cannot be\n"
|
||||
"edited.\n"
|
||||
|
|
@ -866,7 +866,7 @@ Class<db::PCellParameterDeclaration> decl_PCellParameterDeclaration ("db", "PCel
|
|||
gsi::method ("readonly=", &db::PCellParameterDeclaration::set_readonly, gsi::arg ("flag"),
|
||||
"@brief Makes the parameter read-only if this attribute is set to true\n"
|
||||
) +
|
||||
gsi::method_ext ("clear_choices", &clear_choices,
|
||||
gsi::method_ext ("clear_choices", &clear_choices,
|
||||
"@brief Clears the list of choices\n"
|
||||
) +
|
||||
gsi::method_ext ("add_choice", &add_choice, gsi::arg ("description"), gsi::arg ("value"),
|
||||
|
|
@ -875,13 +875,13 @@ Class<db::PCellParameterDeclaration> decl_PCellParameterDeclaration ("db", "PCel
|
|||
"choices. If choices are defined, KLayout will show a drop-down box instead of an\n"
|
||||
"entry field in the parameter user interface.\n"
|
||||
) +
|
||||
gsi::method ("choice_values", &db::PCellParameterDeclaration::get_choices,
|
||||
gsi::method ("choice_values", &db::PCellParameterDeclaration::get_choices,
|
||||
"@brief Returns a list of choice values\n"
|
||||
) +
|
||||
gsi::method ("choice_descriptions", &db::PCellParameterDeclaration::get_choice_descriptions,
|
||||
gsi::method ("choice_descriptions", &db::PCellParameterDeclaration::get_choice_descriptions,
|
||||
"@brief Returns a list of choice descriptions\n"
|
||||
) +
|
||||
gsi::method ("default", &db::PCellParameterDeclaration::get_default,
|
||||
gsi::method ("default", &db::PCellParameterDeclaration::get_default,
|
||||
"@brief Gets the default value\n"
|
||||
) +
|
||||
gsi::method ("default=", &db::PCellParameterDeclaration::set_default, gsi::arg ("value"),
|
||||
|
|
@ -910,4 +910,3 @@ Class<db::PCellParameterDeclaration> decl_PCellParameterDeclaration ("db", "PCel
|
|||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace gsi
|
|||
// point binding
|
||||
|
||||
template <class C>
|
||||
struct point_defs
|
||||
struct point_defs
|
||||
{
|
||||
typedef typename C::coord_type coord_type;
|
||||
|
||||
|
|
@ -141,6 +141,14 @@ struct point_defs
|
|||
"\n"
|
||||
"Starting with version 0.25, this method renders a vector."
|
||||
) +
|
||||
method ("-", (C (C::*) (const db::vector<coord_type> &) const) &C::subtract, gsi::arg ("v"),
|
||||
"@brief Subtract one vector from a point\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"Subtract vector v from from self by subtracting the coordinates. This renders a point.\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.27."
|
||||
) +
|
||||
method ("<", &C::less, gsi::arg ("p"),
|
||||
"@brief \"less\" comparison operator\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -523,6 +523,13 @@ Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveS
|
|||
"The flags must be specified before the shapes are being retrieved.\n"
|
||||
"Settings the shapes flags will reset the iterator.\n"
|
||||
) +
|
||||
gsi::method ("shape_flags", (unsigned int (db::RecursiveShapeIterator::*)() const) &db::RecursiveShapeIterator::shape_flags,
|
||||
"@brief Gets the shape selection flags\n"
|
||||
"\n"
|
||||
"See \\shape_flags= for a description of that property.\n"
|
||||
"\n"
|
||||
"This getter has been introduced in version 0.28.\n"
|
||||
) +
|
||||
gsi::method ("trans|#itrans", &db::RecursiveShapeIterator::trans,
|
||||
"@brief Gets the current transformation by which the shapes must be transformed into the initial cell\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -752,7 +752,7 @@ extern Class<db::ShapeCollection> decl_dbShapeCollection;
|
|||
|
||||
|
||||
Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
||||
constructor ("new", &new_v,
|
||||
constructor ("new", &new_v,
|
||||
"@brief Default constructor\n"
|
||||
"\n"
|
||||
"This constructor creates an empty region.\n"
|
||||
|
|
@ -798,8 +798,8 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"@code\n"
|
||||
"layout = ... # a layout\n"
|
||||
"cell = ... # the index of the initial cell\n"
|
||||
"layer = ... # the index of the layer from where to take the shapes from\n"
|
||||
"cell = ... # the index of the initial cell\n"
|
||||
"layer = ... # the index of the layer from where to take the shapes from\n"
|
||||
"r = RBA::Region::new(layout.begin_shapes(cell, layer))\n"
|
||||
"@/code\n"
|
||||
) +
|
||||
|
|
@ -814,8 +814,8 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"@code\n"
|
||||
"layout = ... # a layout\n"
|
||||
"cell = ... # the index of the initial cell\n"
|
||||
"layer = ... # the index of the layer from where to take the shapes from\n"
|
||||
"cell = ... # the index of the initial cell\n"
|
||||
"layer = ... # the index of the layer from where to take the shapes from\n"
|
||||
"dbu = 0.1 # the target database unit\n"
|
||||
"r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n"
|
||||
"@/code\n"
|
||||
|
|
@ -925,11 +925,11 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"as single regions and artificial edges such as cut-lines will not be considered.\n"
|
||||
"Merged semantics thus is equivalent to considering coherent areas rather than\n"
|
||||
"single polygons\n"
|
||||
) +
|
||||
) +
|
||||
method ("merged_semantics?", &db::Region::merged_semantics,
|
||||
"@brief Gets a flag indicating whether merged semantics is enabled\n"
|
||||
"See \\merged_semantics= for a description of this attribute.\n"
|
||||
) +
|
||||
) +
|
||||
method ("strict_handling=", &db::Region::set_strict_handling, gsi::arg ("f"),
|
||||
"@brief Enables or disables strict handling\n"
|
||||
"\n"
|
||||
|
|
@ -941,7 +941,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"Strict handling is disabled by default and optimization is in place.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.23.2."
|
||||
) +
|
||||
) +
|
||||
method ("strict_handling?", &db::Region::strict_handling,
|
||||
"@brief Gets a flag indicating whether merged semantics is enabled\n"
|
||||
"See \\strict_handling= for a description of this attribute.\n"
|
||||
|
|
@ -957,11 +957,11 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"created which touch at a corner).\n"
|
||||
"\n"
|
||||
"The default setting is maximum coherence (min_coherence = false).\n"
|
||||
) +
|
||||
) +
|
||||
method ("min_coherence?", &db::Region::min_coherence,
|
||||
"@brief Gets a flag indicating whether minimum coherence is selected\n"
|
||||
"See \\min_coherence= for a description of this attribute.\n"
|
||||
) +
|
||||
) +
|
||||
method_ext ("complex_op", &complex_op, gsi::arg ("node"),
|
||||
"@brief Executes a complex operation (see \\CompoundRegionOperationNode for details)\n"
|
||||
"\n"
|
||||
|
|
@ -1337,7 +1337,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"of these boxes for example.\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method_ext ("extents", &extents1, gsi::arg ("d"),
|
||||
"@brief Returns a region with the enlarged bounding boxes of the polygons\n"
|
||||
"This method will return a region consisting of the bounding boxes of the polygons enlarged by the given distance d.\n"
|
||||
|
|
@ -1346,7 +1346,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"of these boxes for example.\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method_ext ("extents", &extents2, gsi::arg ("dx"), gsi::arg ("dy"),
|
||||
"@brief Returns a region with the enlarged bounding boxes of the polygons\n"
|
||||
"This method will return a region consisting of the bounding boxes of the polygons enlarged by the given distance dx in x direction and dy in y direction.\n"
|
||||
|
|
@ -1355,7 +1355,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"of these boxes for example.\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method_ext ("extent_refs", &extent_refs,
|
||||
"@hide\n"
|
||||
"This method is provided for DRC implementation.\n"
|
||||
|
|
@ -1589,7 +1589,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"This method returns the sized region (see \\size), but does not modify self.\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method_ext ("andnot", &andnot, gsi::arg ("other"),
|
||||
"@brief Returns the boolean AND and NOT between self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1607,7 +1607,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This method will compute the boolean AND (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("&=", &db::Region::operator&=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean AND between self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1615,7 +1615,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This method will compute the boolean AND (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("-", &db::Region::operator-, gsi::arg ("other"),
|
||||
"@brief Returns the boolean NOT between self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1623,7 +1623,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This method will compute the boolean NOT (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("-=", &db::Region::operator-=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean NOT between self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1631,15 +1631,15 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This method will compute the boolean NOT (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("^", &db::Region::operator^, gsi::arg ("other"),
|
||||
"@brief Returns the boolean NOT between self and the other region\n"
|
||||
"@brief Returns the boolean XOR between self and the other region\n"
|
||||
"\n"
|
||||
"@return The result of the boolean XOR operation\n"
|
||||
"\n"
|
||||
"This method will compute the boolean XOR (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("^=", &db::Region::operator^=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean XOR between self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1647,7 +1647,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This method will compute the boolean XOR (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("\\|", &db::Region::operator|, gsi::arg ("other"),
|
||||
"@brief Returns the boolean OR between self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1655,7 +1655,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"The boolean OR is implemented by merging the polygons of both regions. To simply join the regions "
|
||||
"without merging, the + operator is more efficient."
|
||||
) +
|
||||
) +
|
||||
method ("\\|=", &db::Region::operator|=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean OR between self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1663,7 +1663,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"The boolean OR is implemented by merging the polygons of both regions. To simply join the regions "
|
||||
"without merging, the + operator is more efficient."
|
||||
) +
|
||||
) +
|
||||
method ("+", &db::Region::operator+, gsi::arg ("other"),
|
||||
"@brief Returns the combined region of self and the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1671,7 +1671,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This operator adds the polygons of the other region to self and returns a new combined region. "
|
||||
"This usually creates unmerged regions and polygons may overlap. Use \\merge if you want to ensure the result region is merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("+=", &db::Region::operator+=, gsi::arg ("other"),
|
||||
"@brief Adds the polygons of the other region to self\n"
|
||||
"\n"
|
||||
|
|
@ -1679,7 +1679,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This operator adds the polygons of the other region to self. "
|
||||
"This usually creates unmerged regions and polygons may overlap. Use \\merge if you want to ensure the result region is merged.\n"
|
||||
) +
|
||||
) +
|
||||
method ("covering", &db::Region::selected_enclosing, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits<size_t>::max ()), "unlimited"),
|
||||
"@brief Returns the polygons of this region which are completely covering polygons from the other region\n"
|
||||
"\n"
|
||||
|
|
@ -1740,14 +1740,14 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"@return A new region containing the polygons which are inside polygons from the other region\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method ("not_inside", &db::Region::selected_not_inside, gsi::arg ("other"),
|
||||
"@brief Returns the polygons of this region which are not completely inside polygons from the other region\n"
|
||||
"\n"
|
||||
"@return A new region containing the polygons which are not inside polygons from the other region\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method_ext ("split_inside", &split_inside, gsi::arg ("other"),
|
||||
"@brief Returns the polygons of this region which are completely inside polygons from the other region and the ones which are not at the same time\n"
|
||||
"\n"
|
||||
|
|
@ -1764,28 +1764,28 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"@return The region after the polygons have been selected (self)\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method ("select_not_inside", &db::Region::select_not_inside, gsi::arg ("other"),
|
||||
"@brief Selects the polygons of this region which are not completely inside polygons from the other region\n"
|
||||
"\n"
|
||||
"@return The region after the polygons have been selected (self)\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method ("outside", &db::Region::selected_outside, gsi::arg ("other"),
|
||||
"@brief Returns the polygons of this region which are completely outside polygons from the other region\n"
|
||||
"\n"
|
||||
"@return A new region containing the polygons which are outside polygons from the other region\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method ("not_outside", &db::Region::selected_not_outside, gsi::arg ("other"),
|
||||
"@brief Returns the polygons of this region which are not completely outside polygons from the other region\n"
|
||||
"\n"
|
||||
"@return A new region containing the polygons which are not outside polygons from the other region\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method_ext ("split_outside", &split_outside, gsi::arg ("other"),
|
||||
"@brief Returns the polygons of this region which are completely outside polygons from the other region and the ones which are not at the same time\n"
|
||||
"\n"
|
||||
|
|
@ -1802,14 +1802,14 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"@return The region after the polygons have been selected (self)\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method ("select_not_outside", &db::Region::select_not_outside, gsi::arg ("other"),
|
||||
"@brief Selects the polygons of this region which are not completely outside polygons from the other region\n"
|
||||
"\n"
|
||||
"@return The region after the polygons have been selected (self)\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
method ("interacting", (db::Region (db::Region::*) (const db::Region &, size_t, size_t) const) &db::Region::selected_interacting, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits<size_t>::max ()), "unlimited"),
|
||||
"@brief Returns the polygons of this region which overlap or touch polygons from the other region\n"
|
||||
"\n"
|
||||
|
|
@ -2020,7 +2020,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
"\n"
|
||||
"The count options have been introduced in version 0.27."
|
||||
) +
|
||||
) +
|
||||
method ("not_overlapping", &db::Region::selected_not_overlapping, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits<size_t>::max ()), "unlimited"),
|
||||
"@brief Returns the polygons of this region which do not overlap polygons from the other region\n"
|
||||
"\n"
|
||||
|
|
@ -2129,7 +2129,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"possibilities of the edge collection.\n"
|
||||
"\n"
|
||||
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
|
||||
) +
|
||||
) +
|
||||
factory_ext ("decompose_convex", &decompose_convex<db::Shapes>, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"),
|
||||
"@brief Decomposes the region into convex pieces.\n"
|
||||
"\n"
|
||||
|
|
@ -2166,7 +2166,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
method ("swap", &db::Region::swap, gsi::arg ("other"),
|
||||
"@brief Swap the contents of this region with the contents of another region\n"
|
||||
"This method is useful to avoid excessive memory allocation in some cases. "
|
||||
"For managed memory languages such as Ruby, those cases will be rare. "
|
||||
"For managed memory languages such as Ruby, those cases will be rare. "
|
||||
) +
|
||||
method ("holes", &db::Region::holes,
|
||||
"@brief Returns the holes of the region\n"
|
||||
|
|
@ -3091,4 +3091,3 @@ gsi::EnumIn<db::Region, db::OppositeFilter> decl_Region_OppositeFilter ("db", "O
|
|||
gsi::ClassExt<db::Region> inject_OppositeFilter_in_parent (decl_Region_OppositeFilter.defs ());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ struct trans_defs
|
|||
"\n"
|
||||
"This convenience method has been introduced in version 0.25."
|
||||
) +
|
||||
method ("*", &C::concat, arg ("t"),
|
||||
method ("*!", &C::concat, arg ("t"),
|
||||
"@brief Returns the concatenated transformation\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
@ -829,7 +829,7 @@ struct cplx_trans_defs
|
|||
"\n"
|
||||
"This convenience method has been introduced in version 0.25."
|
||||
) +
|
||||
method ("*", (C (C::*) (const C &c) const) &C::concat_same, arg ("t"),
|
||||
method ("*!", (C (C::*) (const C &c) const) &C::concat_same, arg ("t"),
|
||||
"@brief Returns the concatenated transformation\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
@ -1047,7 +1047,7 @@ Class<db::DCplxTrans> decl_DCplxTrans ("db", "DCplxTrans",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25."
|
||||
) +
|
||||
method ("*", (db::CplxTrans (db::DCplxTrans::*) (const db::CplxTrans &) const) &db::DCplxTrans::concat, gsi::arg ("t"),
|
||||
method ("*!", (db::CplxTrans (db::DCplxTrans::*) (const db::CplxTrans &) const) &db::DCplxTrans::concat, gsi::arg ("t"),
|
||||
"@brief Multiplication (concatenation) of transformations\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
@ -1126,7 +1126,7 @@ Class<db::CplxTrans> decl_CplxTrans ("db", "CplxTrans",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25."
|
||||
) +
|
||||
method ("*", (db::DCplxTrans (db::CplxTrans::*) (const db::VCplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"),
|
||||
method ("*!", (db::DCplxTrans (db::CplxTrans::*) (const db::VCplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"),
|
||||
"@brief Multiplication (concatenation) of transformations\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
@ -1134,7 +1134,7 @@ Class<db::CplxTrans> decl_CplxTrans ("db", "CplxTrans",
|
|||
"@param t The transformation to apply before\n"
|
||||
"@return The modified transformation\n"
|
||||
) +
|
||||
method ("*", (db::CplxTrans (db::CplxTrans::*) (const db::ICplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"),
|
||||
method ("*!", (db::CplxTrans (db::CplxTrans::*) (const db::ICplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"),
|
||||
"@brief Multiplication (concatenation) of transformations\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
@ -1216,7 +1216,7 @@ Class<db::ICplxTrans> decl_ICplxTrans ("db", "ICplxTrans",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25."
|
||||
) +
|
||||
method ("*", (db::VCplxTrans (db::ICplxTrans::*) (const db::VCplxTrans &) const) &db::ICplxTrans::concat, gsi::arg ("t"),
|
||||
method ("*!", (db::VCplxTrans (db::ICplxTrans::*) (const db::VCplxTrans &) const) &db::ICplxTrans::concat, gsi::arg ("t"),
|
||||
"@brief Multiplication (concatenation) of transformations\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
@ -1288,7 +1288,7 @@ Class<db::VCplxTrans> decl_VCplxTrans ("db", "VCplxTrans",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25."
|
||||
) +
|
||||
method ("*", (db::VCplxTrans (db::VCplxTrans::*) (const db::DCplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"),
|
||||
method ("*!", (db::VCplxTrans (db::VCplxTrans::*) (const db::DCplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"),
|
||||
"@brief Multiplication (concatenation) of transformations\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
@ -1296,7 +1296,7 @@ Class<db::VCplxTrans> decl_VCplxTrans ("db", "VCplxTrans",
|
|||
"@param t The transformation to apply before\n"
|
||||
"@return The modified transformation\n"
|
||||
) +
|
||||
method ("*", (db::ICplxTrans (db::VCplxTrans::*) (const db::CplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"),
|
||||
method ("*!", (db::ICplxTrans (db::VCplxTrans::*) (const db::CplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"),
|
||||
"@brief Multiplication (concatenation) of transformations\n"
|
||||
"\n"
|
||||
"The * operator returns self*t (\"t is applied before this transformation\").\n"
|
||||
|
|
|
|||
|
|
@ -246,6 +246,12 @@ struct vector_defs
|
|||
"\n"
|
||||
"The scalar product of a and b is defined as: vp = ax*bx+ay*by.\n"
|
||||
) +
|
||||
method_ext ("*", &sprod, gsi::arg ("v"),
|
||||
"@brief Computes the scalar product between self and the given vector\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"The scalar product of a and b is defined as: vp = ax*bx+ay*by.\n"
|
||||
) +
|
||||
method_ext ("sprod_sign", &sprod_sign, gsi::arg ("v"),
|
||||
"@brief Computes the scalar product between self and the given vector and returns a value indicating the sign of the product\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ static int t_double () { return T_double; }
|
|||
static int t_float () { return T_float; }
|
||||
static int t_var () { return T_var; }
|
||||
static int t_string () { return T_string; }
|
||||
static int t_byte_array () { return T_byte_array; }
|
||||
static int t_void_ptr () { return T_void_ptr; }
|
||||
static int t_object () { return T_object; }
|
||||
static int t_vector () { return T_vector; }
|
||||
|
|
@ -77,30 +78,31 @@ static tl::Variant default_value (const ArgType *t)
|
|||
}
|
||||
|
||||
Class<ArgType> decl_ArgType ("tl", "ArgType",
|
||||
gsi::method ("TypeVoid|#t_void", &t_void) +
|
||||
gsi::method ("TypeBool|#t_bool", &t_bool) +
|
||||
gsi::method ("TypeChar|#t_char", &t_char) +
|
||||
gsi::method ("TypeSChar|#t_schar", &t_schar) +
|
||||
gsi::method ("TypeUChar|#t_uchar", &t_uchar) +
|
||||
gsi::method ("TypeShort|#t_short", &t_short) +
|
||||
gsi::method ("TypeUShort|#t_ushort", &t_ushort) +
|
||||
gsi::method ("TypeInt|#t_int", &t_int) +
|
||||
gsi::method ("TypeUInt|#t_uint", &t_uint) +
|
||||
gsi::method ("TypeLong|#t_long", &t_long) +
|
||||
gsi::method ("TypeULong|#t_ulong", &t_ulong) +
|
||||
gsi::method ("TypeLongLong|#t_longlong", &t_longlong) +
|
||||
gsi::method ("TypeULongLong|#t_ulonglong", &t_ulonglong) +
|
||||
gsi::method ("TypeVoid", &t_void) +
|
||||
gsi::method ("TypeBool", &t_bool) +
|
||||
gsi::method ("TypeChar", &t_char) +
|
||||
gsi::method ("TypeSChar", &t_schar) +
|
||||
gsi::method ("TypeUChar", &t_uchar) +
|
||||
gsi::method ("TypeShort", &t_short) +
|
||||
gsi::method ("TypeUShort", &t_ushort) +
|
||||
gsi::method ("TypeInt", &t_int) +
|
||||
gsi::method ("TypeUInt", &t_uint) +
|
||||
gsi::method ("TypeLong", &t_long) +
|
||||
gsi::method ("TypeULong", &t_ulong) +
|
||||
gsi::method ("TypeLongLong", &t_longlong) +
|
||||
gsi::method ("TypeULongLong", &t_ulonglong) +
|
||||
#if defined(HAVE_64BIT_COORD)
|
||||
gsi::method ("TypeInt128|#t_int128", &t_int128) +
|
||||
#endif
|
||||
gsi::method ("TypeDouble|#t_double", &t_double) +
|
||||
gsi::method ("TypeFloat|#t_float", &t_float) +
|
||||
gsi::method ("TypeVar|#t_var", &t_var) +
|
||||
gsi::method ("TypeString|#t_string", &t_string) +
|
||||
gsi::method ("TypeVoidPtr|#t_void_ptr", &t_void_ptr) +
|
||||
gsi::method ("TypeObject|#t_object", &t_object) +
|
||||
gsi::method ("TypeVector|#t_vector", &t_vector) +
|
||||
gsi::method ("TypeMap|#t_map", &t_map) +
|
||||
gsi::method ("TypeDouble", &t_double) +
|
||||
gsi::method ("TypeFloat", &t_float) +
|
||||
gsi::method ("TypeVar", &t_var) +
|
||||
gsi::method ("TypeByteArray", &t_byte_array) +
|
||||
gsi::method ("TypeString", &t_string) +
|
||||
gsi::method ("TypeVoidPtr", &t_void_ptr) +
|
||||
gsi::method ("TypeObject", &t_object) +
|
||||
gsi::method ("TypeVector", &t_vector) +
|
||||
gsi::method ("TypeMap", &t_map) +
|
||||
gsi::method_ext ("type", &type,
|
||||
"@brief Return the basic type (see t_.. constants)\n"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -199,6 +199,9 @@ private:
|
|||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||
if (syn->is_setter) {
|
||||
add_method (syn->name + "=", *m);
|
||||
} else if (syn->name == "*!") {
|
||||
// non-commutative multiplication
|
||||
add_method ("*", *m);
|
||||
} else {
|
||||
add_method (syn->name, *m);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ void MethodBase::parse_name (const std::string &name)
|
|||
{
|
||||
const char *n = name.c_str ();
|
||||
|
||||
if (*n == '*' && n[1] && n[1] != '*' && n[1] != '=') {
|
||||
if (*n == '*' && n[1] && n[1] != '*' && n[1] != '!' && n[1] != '=') {
|
||||
m_protected = true;
|
||||
++n;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -589,3 +589,12 @@ TEST(10)
|
|||
EXPECT_EQ (v.to_string (), std::string ("2"));
|
||||
}
|
||||
|
||||
TEST(11)
|
||||
{
|
||||
tl::Eval e;
|
||||
tl::Variant v;
|
||||
|
||||
// mapping of *! to *:
|
||||
v = e.parse ("var b = Trans.new(1)*Trans.new(Vector.new(10, 20))").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("r90 -20,10"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,8 @@ full_name (const gsi::MethodBase::MethodSynonym &syn)
|
|||
return syn.name + "?";
|
||||
} else if (syn.is_setter) {
|
||||
return syn.name + "=";
|
||||
} else if (syn.name == "*!") {
|
||||
return "*";
|
||||
} else {
|
||||
return syn.name;
|
||||
}
|
||||
|
|
@ -1411,7 +1413,7 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
|
|||
if (! pydoc.empty ()) {
|
||||
os << "<p><b>";
|
||||
os << tl::to_string (QObject::tr ("Python specific notes: "));
|
||||
os << "</b><br/>" << escape_xml (pydoc) << "</p>" << std::endl;
|
||||
os << "</b><br/>" << tl::replaced (escape_xml (pydoc), "\n\n", "<br/>") << "</p>" << std::endl;
|
||||
}
|
||||
|
||||
os << "</td></tr>";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2022 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "gsiDecl.h"
|
||||
#include "pyaInternal.h"
|
||||
#include "pya.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
||||
static const pya::MethodTableEntry *getter (std::pair<const pya::MethodTableEntry *, const pya::MethodTableEntry *> *p)
|
||||
{
|
||||
return p->second;
|
||||
}
|
||||
|
||||
static const pya::MethodTableEntry *setter (std::pair<const pya::MethodTableEntry *, const pya::MethodTableEntry *> *p)
|
||||
{
|
||||
return p->first;
|
||||
}
|
||||
|
||||
gsi::Class<std::pair<const pya::MethodTableEntry *, const pya::MethodTableEntry *> > decl_PythonGetterSetterPair ("tl", "PythonGetterSetterPair",
|
||||
gsi::method_ext ("getter", &getter, "@brief Gets the getter function") +
|
||||
gsi::method_ext ("setter", &setter, "@brief Gets the setter function"),
|
||||
"@hide"
|
||||
);
|
||||
|
||||
gsi::Class<pya::MethodTableEntry> decl_PythonFunction ("tl", "PythonFunction",
|
||||
gsi::method ("methods", &pya::MethodTableEntry::methods, "@brief Gets the list of methods bound to this Python function") +
|
||||
gsi::method ("name", &pya::MethodTableEntry::name, "@brief Gets the name of this Python function") +
|
||||
gsi::method ("is_static", &pya::MethodTableEntry::is_static, "@brief Gets the value indicating whether this Python function is 'static' (class function)") +
|
||||
gsi::method ("is_protected", &pya::MethodTableEntry::is_protected, "@brief Gets a value indicating whether this function is protected"),
|
||||
"@hide"
|
||||
);
|
||||
|
||||
static std::vector<const pya::MethodTableEntry *> get_python_methods (const gsi::ClassBase *cls, bool st)
|
||||
{
|
||||
const pya::MethodTable *mt = pya::MethodTable::method_table_by_class (cls);
|
||||
|
||||
std::vector<const pya::MethodTableEntry *> methods;
|
||||
|
||||
if (mt != 0) {
|
||||
for (auto m = mt->method_table ().begin (); m != mt->method_table ().end (); ++m) {
|
||||
if (m->is_enabled () && m->is_static () == st) {
|
||||
methods.push_back (m.operator-> ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<const pya::MethodTableEntry *, const pya::MethodTableEntry *> > get_python_properties (const gsi::ClassBase *cls, bool st)
|
||||
{
|
||||
const pya::MethodTable *mt = pya::MethodTable::method_table_by_class (cls);
|
||||
|
||||
std::vector<std::pair<const pya::MethodTableEntry *, const pya::MethodTableEntry *> > methods;
|
||||
|
||||
if (mt != 0) {
|
||||
for (auto m = mt->property_table ().begin (); m != mt->property_table ().end (); ++m) {
|
||||
if (m->first.is_enabled () && m->first.is_static () == st) {
|
||||
methods.push_back (std::make_pair (&m->first, &m->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
static
|
||||
gsi::ClassExt<gsi::ClassBase> class_base_ext (
|
||||
gsi::method_ext ("python_methods", &get_python_methods, gsi::arg ("static"), "@brief Gets the Python methods (static or non-static)") +
|
||||
gsi::method_ext ("python_properties", &get_python_properties, gsi::arg ("static"), "@brief Gets the Python properties (static or non-static) as a list of getter/setter pairs\nNote that if a getter or setter is not available the list of Python functions for this part is empty."),
|
||||
"@hide"
|
||||
);
|
||||
|
||||
static
|
||||
gsi::ClassExt<gsi::MethodBase> method_base_ext (
|
||||
gsi::method_ext ("python_methods", &pya::PythonInterpreter::python_doc, "@brief Gets the Python specific documentation"),
|
||||
"@hide"
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -68,7 +68,7 @@ namespace pya
|
|||
} catch (...) { \
|
||||
if (PythonInterpreter::instance ()) { PythonInterpreter::instance ()->end_execution (); } \
|
||||
throw; \
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A class encapsulating a python exception
|
||||
|
|
@ -92,7 +92,7 @@ class PYA_PUBLIC PythonInterpreter
|
|||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
* @brief The constructor
|
||||
*
|
||||
* If embedded is true, the interpreter is an embedded one. Only in this case, the
|
||||
* Python interpreter is initialized. Otherwise, it is assumed the interpreter
|
||||
|
|
@ -101,7 +101,7 @@ public:
|
|||
PythonInterpreter (bool embedded = true);
|
||||
|
||||
/**
|
||||
* @brief The destructor
|
||||
* @brief The destructor
|
||||
*/
|
||||
~PythonInterpreter ();
|
||||
|
||||
|
|
@ -160,10 +160,10 @@ public:
|
|||
* @brief Implementation of gsi::Interpreter::eval_expr
|
||||
*/
|
||||
tl::Variant eval_expr (const char *string, const char *filename = 0, int line = 1, int context = -1);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Implementation of gsi::Interpreter::eval_string_and_print
|
||||
*/
|
||||
*/
|
||||
void eval_string_and_print (const char *string, const char *filename = 0, int line = 1, int context = -1);
|
||||
|
||||
/**
|
||||
|
|
@ -172,7 +172,7 @@ public:
|
|||
virtual gsi::Inspector *inspector (int context = -1);
|
||||
|
||||
/**
|
||||
* @brief Defines a global variable with the given name and value
|
||||
* @brief Defines a global variable with the given name and value
|
||||
*/
|
||||
void define_variable (const std::string &name, const tl::Variant &value);
|
||||
|
||||
|
|
@ -284,4 +284,3 @@ private:
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,16 @@ SOURCES = \
|
|||
pyaConvert.cc \
|
||||
pyaHelpers.cc \
|
||||
pyaInspector.cc \
|
||||
pyaInternal.cc \
|
||||
pyaCallables.cc \
|
||||
pyaMarshal.cc \
|
||||
pyaObject.cc \
|
||||
pyaRefs.cc \
|
||||
pyaUtils.cc \
|
||||
pyaModule.cc \
|
||||
pyaSignalHandler.cc \
|
||||
pyaStatusChangedListener.cc
|
||||
pyaStatusChangedListener.cc \
|
||||
gsiDeclPya.cc
|
||||
|
||||
HEADERS += \
|
||||
pya.h \
|
||||
|
|
@ -25,6 +28,8 @@ HEADERS += \
|
|||
pyaConvert.h \
|
||||
pyaHelpers.h \
|
||||
pyaInspector.h \
|
||||
pyaInternal.h \
|
||||
pyaCallables.h \
|
||||
pyaMarshal.h \
|
||||
pyaObject.h \
|
||||
pyaRefs.h \
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2022 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HDR_pyaCallables
|
||||
#define _HDR_pyaCallables
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "pyaCommon.h"
|
||||
|
||||
namespace pya
|
||||
{
|
||||
|
||||
void pya_object_deallocate (PyObject *self);
|
||||
int pya_object_init (PyObject * /*self*/, PyObject *args, PyObject *kwds);
|
||||
PyObject *pya_object_new (PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/);
|
||||
|
||||
PyObject *object_default_ne_impl (PyObject *self, PyObject *args);
|
||||
PyObject *object_default_ge_impl (PyObject *self, PyObject *args);
|
||||
PyObject *object_default_le_impl (PyObject *self, PyObject *args);
|
||||
PyObject *object_default_gt_impl (PyObject *self, PyObject *args);
|
||||
PyObject *object_default_deepcopy_impl (PyObject *self, PyObject *args);
|
||||
|
||||
typedef PyObject *(*py_func_ptr_t) (PyObject *, PyObject *);
|
||||
|
||||
py_func_ptr_t get_method_adaptor (int n);
|
||||
py_func_ptr_t get_property_getter_adaptor (int n);
|
||||
py_func_ptr_t get_property_setter_adaptor (int n);
|
||||
py_func_ptr_t get_method_init_adaptor (int n);
|
||||
|
||||
inline void *make_closure (int mid_getter, int mid_setter)
|
||||
{
|
||||
size_t g = mid_getter < 0 ? (size_t) 0 : (size_t) mid_getter;
|
||||
size_t s = mid_setter < 0 ? (size_t) 0 : (size_t) mid_setter;
|
||||
return (void *) ((s << 16) | g);
|
||||
}
|
||||
|
||||
inline unsigned int getter_from_closure (void *closure)
|
||||
{
|
||||
return (unsigned int) (size_t (closure) & 0xffff);
|
||||
}
|
||||
|
||||
inline unsigned int setter_from_closure (void *closure)
|
||||
{
|
||||
return (unsigned int) (size_t (closure) >> 16);
|
||||
}
|
||||
|
||||
PyObject *property_getter_func (PyObject *self, void *closure);
|
||||
int property_setter_func (PyObject *self, PyObject *value, void *closure);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -45,14 +45,14 @@ long python2c_func<long>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check (rval)) {
|
||||
return PyInt_AsLong (rval);
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyLong_Check (rval)) {
|
||||
return PyLong_AsLong (rval);
|
||||
} else if (PyFloat_Check (rval)) {
|
||||
return (long) (PyFloat_AsDouble (rval));
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,14 +68,14 @@ char python2c_func<char>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check (rval)) {
|
||||
return char (PyInt_AsLong (rval));
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyLong_Check (rval)) {
|
||||
return char (PyLong_AsLong (rval));
|
||||
} else if (PyFloat_Check (rval)) {
|
||||
return char (PyFloat_AsDouble (rval));
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a character")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to a character")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,14 +85,14 @@ unsigned long python2c_func<unsigned long>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check (rval)) {
|
||||
return PyInt_AsUnsignedLongMask (rval);
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyLong_Check (rval)) {
|
||||
return PyLong_AsUnsignedLongMask (rval);
|
||||
} else if (PyFloat_Check (rval)) {
|
||||
return (unsigned long) (PyFloat_AsDouble (rval));
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,14 +102,14 @@ long long python2c_func<long long>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check (rval)) {
|
||||
return PyInt_AsLong (rval);
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyLong_Check (rval)) {
|
||||
return PyLong_AsLongLong (rval);
|
||||
} else if (PyFloat_Check (rval)) {
|
||||
return (long long) (PyFloat_AsDouble (rval));
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,14 +119,14 @@ unsigned long long python2c_func<unsigned long long>::operator() (PyObject *rval
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check (rval)) {
|
||||
return PyInt_AsUnsignedLongMask (rval);
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyLong_Check (rval)) {
|
||||
return PyLong_AsUnsignedLongLongMask (rval);
|
||||
} else if (PyFloat_Check (rval)) {
|
||||
return (unsigned long long) (PyFloat_AsDouble (rval));
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,14 +138,14 @@ __int128 python2c_func<__int128>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check (rval)) {
|
||||
return PyInt_AsLong (rval);
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyLong_Check (rval)) {
|
||||
return PyLong_AsLongLong (rval);
|
||||
} else if (PyFloat_Check (rval)) {
|
||||
return PyFloat_AsDouble (rval);
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to an integer")));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -156,14 +156,14 @@ double python2c_func<double>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check (rval)) {
|
||||
return PyInt_AsLong (rval);
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyLong_Check (rval)) {
|
||||
return PyLong_AsLongLong (rval);
|
||||
} else if (PyFloat_Check (rval)) {
|
||||
return PyFloat_AsDouble (rval);
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a floating-point value")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to a floating-point value")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,11 +173,11 @@ std::string python2c_func<std::string>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyString_Check (rval)) {
|
||||
return std::string (PyString_AsString (rval), PyString_Size (rval));
|
||||
} else
|
||||
} else
|
||||
#else
|
||||
if (PyBytes_Check (rval)) {
|
||||
return std::string (PyBytes_AsString (rval), PyBytes_Size (rval));
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyUnicode_Check (rval)) {
|
||||
PythonRef ba (PyUnicode_AsUTF8String (rval));
|
||||
|
|
@ -188,7 +188,7 @@ std::string python2c_func<std::string>::operator() (PyObject *rval)
|
|||
} else if (PyByteArray_Check (rval)) {
|
||||
return std::string (PyByteArray_AsString (rval), PyByteArray_Size (rval));
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a string")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to a string")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ std::vector<char> python2c_func<std::vector<char> >::operator() (PyObject *rval)
|
|||
Py_ssize_t sz = PyByteArray_Size (rval);
|
||||
return std::vector<char> (cp, cp + sz);
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a byte array")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to a byte array")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,11 +235,11 @@ QByteArray python2c_func<QByteArray>::operator() (PyObject *rval)
|
|||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyString_Check (rval)) {
|
||||
return QByteArray (PyString_AsString (rval), PyString_Size (rval));
|
||||
} else
|
||||
} else
|
||||
#else
|
||||
if (PyBytes_Check (rval)) {
|
||||
return QByteArray (PyBytes_AsString (rval), PyBytes_Size (rval));
|
||||
} else
|
||||
} else
|
||||
#endif
|
||||
if (PyUnicode_Check (rval)) {
|
||||
PythonRef ba (PyUnicode_AsUTF8String (rval));
|
||||
|
|
@ -250,7 +250,7 @@ QByteArray python2c_func<QByteArray>::operator() (PyObject *rval)
|
|||
} else if (PyByteArray_Check (rval)) {
|
||||
return QByteArray (PyByteArray_AsString (rval), PyByteArray_Size (rval));
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a byte array")));
|
||||
throw tl::TypeError (tl::to_string (tr ("Value cannot be converted to a byte array")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,13 +325,13 @@ tl::Variant python2c_func<tl::Variant>::operator() (PyObject *rval)
|
|||
return r;
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
const gsi::ClassBase *cls = PythonModule::cls_for_type (Py_TYPE (rval));
|
||||
if (cls) {
|
||||
|
||||
PYAObjectBase *p = PYAObjectBase::from_pyobject (rval);
|
||||
|
||||
// employ the tl::Variant binding capabilities of the Expression binding to derive the
|
||||
// employ the tl::Variant binding capabilities of the Expression binding to derive the
|
||||
// variant value.
|
||||
|
||||
void *obj = p->obj ();
|
||||
|
|
@ -519,7 +519,7 @@ PyObject *c2python_func<const tl::Variant &>::operator() (const tl::Variant &c)
|
|||
PyDict_SetItem (ret, c2python (i->first), c2python (i->second));
|
||||
}
|
||||
return ret;
|
||||
|
||||
|
||||
} else if (c.is_list ()) {
|
||||
|
||||
PyObject *ret = PyList_New (c.get_list ().size ());
|
||||
|
|
@ -619,4 +619,3 @@ PyObject *c2python_func<const QString &>::operator() (const QString &qs)
|
|||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,885 @@
|
|||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2022 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "pya.h"
|
||||
#include "pyaInternal.h"
|
||||
#include "pyaModule.h"
|
||||
#include "pyaObject.h"
|
||||
|
||||
#include "tlLog.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace pya
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// MethodTableEntry implementation
|
||||
|
||||
MethodTableEntry::MethodTableEntry (const std::string &name, bool st, bool prot)
|
||||
: m_name (name), m_is_static (st), m_is_protected (prot), m_is_enabled (true), m_is_init (false), m_fallback_not_implemented (false)
|
||||
{ }
|
||||
|
||||
const std::string &
|
||||
MethodTableEntry::name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void
|
||||
MethodTableEntry::set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
}
|
||||
|
||||
void
|
||||
MethodTableEntry::set_enabled (bool en)
|
||||
{
|
||||
m_is_enabled = en;
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTableEntry::is_enabled () const
|
||||
{
|
||||
return m_is_enabled;
|
||||
}
|
||||
|
||||
void
|
||||
MethodTableEntry::set_fallback_not_implemented (bool f)
|
||||
{
|
||||
m_fallback_not_implemented = f;
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTableEntry::fallback_not_implemented () const
|
||||
{
|
||||
return m_fallback_not_implemented;
|
||||
}
|
||||
|
||||
void
|
||||
MethodTableEntry::set_init (bool f)
|
||||
{
|
||||
m_is_init = f;
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTableEntry::is_init () const
|
||||
{
|
||||
return m_is_init;
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTableEntry::is_static () const
|
||||
{
|
||||
return m_is_static;
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTableEntry::is_protected () const
|
||||
{
|
||||
return m_is_protected;
|
||||
}
|
||||
|
||||
void
|
||||
MethodTableEntry::add (const gsi::MethodBase *m)
|
||||
{
|
||||
m_methods.push_back (m);
|
||||
}
|
||||
|
||||
void
|
||||
MethodTableEntry::finish ()
|
||||
{
|
||||
// remove duplicate entries in the method list
|
||||
std::vector<const gsi::MethodBase *> m = m_methods;
|
||||
std::sort(m.begin (), m.end ());
|
||||
m_methods.assign (m.begin (), std::unique (m.begin (), m.end ()));
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTableEntry::begin () const
|
||||
{
|
||||
return m_methods.begin ();
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTableEntry::end () const
|
||||
{
|
||||
return m_methods.end ();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// MethodTable implementation
|
||||
|
||||
MethodTable::MethodTable (const gsi::ClassBase *cls_decl, PythonModule *module)
|
||||
: m_method_offset (0), m_property_offset (0), mp_cls_decl (cls_decl), mp_module (module)
|
||||
{
|
||||
if (cls_decl->base ()) {
|
||||
const MethodTable *base_mt = method_table_by_class (cls_decl->base ());
|
||||
tl_assert (base_mt);
|
||||
m_method_offset = base_mt->top_mid ();
|
||||
m_property_offset = base_mt->top_property_mid ();
|
||||
}
|
||||
|
||||
// signals are translated into the setters and getters
|
||||
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
||||
if ((*m)->is_signal ()) {
|
||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||
add_getter (syn->name, *m);
|
||||
add_setter (syn->name, *m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// first add getters and setters
|
||||
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
||||
if (! (*m)->is_callback ()) {
|
||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||
if (syn->is_getter) {
|
||||
add_getter (syn->name, *m);
|
||||
} else if (syn->is_setter) {
|
||||
add_setter (syn->name, *m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then add normal methods - on name clash with properties make them a getter
|
||||
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
||||
|
||||
if (! (*m)->is_callback () && ! (*m)->is_signal ()) {
|
||||
|
||||
bool st = (*m)->is_static ();
|
||||
bool no_args = ((*m)->end_arguments () == (*m)->begin_arguments ());
|
||||
|
||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||
if (! syn->is_getter && ! syn->is_setter) {
|
||||
if (no_args && is_property_setter (st, syn->name) && ! is_property_getter (st, syn->name)) {
|
||||
add_getter (syn->name, *m);
|
||||
} else if (st && no_args && (isupper (syn->name [0]) || (*m)->is_const ())) {
|
||||
// static methods without arguments which start with a capital letter are treated as constants
|
||||
add_getter (syn->name, *m);
|
||||
} else {
|
||||
add_method (syn->name, *m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// synthesize a getter from is_...? predicates (e.g. is_empty? -> empty getter)
|
||||
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
||||
|
||||
if (! (*m)->is_callback () && ! (*m)->is_signal ()) {
|
||||
|
||||
bool st = (*m)->is_static ();
|
||||
bool no_args = ((*m)->end_arguments () == (*m)->begin_arguments ());
|
||||
|
||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
||||
if (no_args && ! syn->is_getter && ! syn->is_setter && syn->is_predicate && std::string (syn->name, 0, 3) == "is_") {
|
||||
std::string n = std::string (syn->name, 3, std::string::npos);
|
||||
if (is_property_setter (st, n) && ! is_property_getter (st, n)) {
|
||||
add_getter (n, *m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
MethodTable::bottom_mid () const
|
||||
{
|
||||
return m_method_offset;
|
||||
}
|
||||
|
||||
size_t
|
||||
MethodTable::top_mid () const
|
||||
{
|
||||
return m_method_offset + m_table.size ();
|
||||
}
|
||||
|
||||
size_t
|
||||
MethodTable::bottom_property_mid () const
|
||||
{
|
||||
return m_property_offset;
|
||||
}
|
||||
|
||||
size_t
|
||||
MethodTable::top_property_mid () const
|
||||
{
|
||||
return m_property_offset + m_property_table.size ();
|
||||
}
|
||||
|
||||
std::pair<bool, size_t>
|
||||
MethodTable::find_method (bool st, const std::string &name) const
|
||||
{
|
||||
std::map <std::pair<bool, std::string>, size_t>::const_iterator t = m_name_map.find (std::make_pair (st, name));
|
||||
if (t != m_name_map.end ()) {
|
||||
return std::make_pair (true, t->second + m_method_offset);
|
||||
} else {
|
||||
return std::make_pair (false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, size_t>
|
||||
MethodTable::find_property (bool st, const std::string &name) const
|
||||
{
|
||||
std::map <std::pair<bool, std::string>, size_t>::const_iterator t = m_property_name_map.find (std::make_pair (st, name));
|
||||
if (t != m_property_name_map.end ()) {
|
||||
return std::make_pair (true, t->second + m_property_offset);
|
||||
} else {
|
||||
return std::make_pair (false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTable::is_property_setter (bool st, const std::string &name)
|
||||
{
|
||||
std::pair<bool, size_t> p = find_property (st, name);
|
||||
if (! p.first) {
|
||||
return false;
|
||||
}
|
||||
return (begin_setters (p.second) != end_setters (p.second));
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTable::is_property_getter (bool st, const std::string &name)
|
||||
{
|
||||
std::pair<bool, size_t> p = find_property (st, name);
|
||||
if (! p.first) {
|
||||
return false;
|
||||
}
|
||||
return (begin_getters (p.second) != end_getters (p.second));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the name is a reserved keyword
|
||||
*/
|
||||
static bool is_reserved_word (const std::string &name)
|
||||
{
|
||||
return (name == "and" ||
|
||||
name == "del" ||
|
||||
name == "from" ||
|
||||
name == "not" ||
|
||||
name == "while" ||
|
||||
name == "as" ||
|
||||
name == "elif" ||
|
||||
name == "global" ||
|
||||
name == "or" ||
|
||||
name == "with" ||
|
||||
name == "assert" ||
|
||||
name == "else" ||
|
||||
name == "if" ||
|
||||
name == "pass" ||
|
||||
name == "yield" ||
|
||||
name == "break" ||
|
||||
name == "except" ||
|
||||
name == "import" ||
|
||||
name == "print" ||
|
||||
name == "class" ||
|
||||
name == "exec" ||
|
||||
name == "in" ||
|
||||
name == "raise" ||
|
||||
name == "continue" ||
|
||||
name == "finally" ||
|
||||
name == "is" ||
|
||||
name == "return" ||
|
||||
name == "def" ||
|
||||
name == "for" ||
|
||||
name == "lambda" ||
|
||||
name == "try" ||
|
||||
name == "None");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts the Python name from a generic name
|
||||
*
|
||||
* Returns an empty string if no Python name could be generated.
|
||||
*/
|
||||
static std::string extract_python_name (const std::string &name)
|
||||
{
|
||||
// some operator replacements
|
||||
if (name == "++") {
|
||||
return "inc";
|
||||
} else if (name == "--") {
|
||||
return "dec";
|
||||
} else if (name == "()") {
|
||||
return "call";
|
||||
} else if (name == "!") {
|
||||
return "not";
|
||||
} else if (name == "==") {
|
||||
return "__eq__";
|
||||
} else if (name == "!=") {
|
||||
return "__ne__";
|
||||
} else if (name == "<") {
|
||||
return "__lt__";
|
||||
} else if (name == "<=") {
|
||||
return "__le__";
|
||||
} else if (name == ">") {
|
||||
return "__gt__";
|
||||
} else if (name == ">=") {
|
||||
return "__ge__";
|
||||
} else if (name == "<=>") {
|
||||
return "__cmp__";
|
||||
} else if (name == "+") {
|
||||
return "__add__";
|
||||
} else if (name == "+@") {
|
||||
return "__pos__";
|
||||
} else if (name == "-") {
|
||||
return "__sub__";
|
||||
} else if (name == "-@") {
|
||||
return "__neg__";
|
||||
} else if (name == "/") {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
return "__div__";
|
||||
#else
|
||||
return "__truediv__";
|
||||
#endif
|
||||
} else if (name == "*" || name == "*!") {
|
||||
return "__mul__";
|
||||
} else if (name == "%") {
|
||||
return "__mod__";
|
||||
} else if (name == "<<") {
|
||||
return "__lshift__";
|
||||
} else if (name == ">>") {
|
||||
return "__rshift__";
|
||||
} else if (name == "~") {
|
||||
return "__invert__";
|
||||
} else if (name == "&") {
|
||||
return "__and__";
|
||||
} else if (name == "|") {
|
||||
return "__or__";
|
||||
} else if (name == "^") {
|
||||
return "__xor__";
|
||||
} else if (name == "+=") {
|
||||
return "__iadd__";
|
||||
} else if (name == "-=") {
|
||||
return "__isub__";
|
||||
} else if (name == "/=") {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
return "__idiv__";
|
||||
#else
|
||||
return "__itruediv__";
|
||||
#endif
|
||||
} else if (name == "*=") {
|
||||
return "__imul__";
|
||||
} else if (name == "%=") {
|
||||
return "__imod__";
|
||||
} else if (name == "<<=") {
|
||||
return "__ilshift__";
|
||||
} else if (name == ">>=") {
|
||||
return "__irshift__";
|
||||
} else if (name == "&=") {
|
||||
return "__iand__";
|
||||
} else if (name == "|=") {
|
||||
return "__ior__";
|
||||
} else if (name == "^=") {
|
||||
return "__ixor__";
|
||||
} else if (name == "[]") {
|
||||
return "__getitem__";
|
||||
} else if (name == "[]=") {
|
||||
return "__setitem__";
|
||||
} else {
|
||||
|
||||
const char *c = name.c_str ();
|
||||
if (! isalnum (*c) && *c != '_') {
|
||||
return std::string ();
|
||||
}
|
||||
|
||||
// question-mark symbol and trailing = are removed.
|
||||
size_t n = 0;
|
||||
for ( ; *c; ++c) {
|
||||
if (*c == '=' || *c == '?') {
|
||||
if (! c[1]) {
|
||||
if (*c == '=') {
|
||||
// Normally, this method is replaced by an attribute.
|
||||
// If that fails, we prepend a "set_" to make the name unique.
|
||||
return "set_" + std::string (name, 0, n);
|
||||
} else {
|
||||
return std::string (name, 0, n);
|
||||
}
|
||||
} else {
|
||||
return std::string ();
|
||||
}
|
||||
} else if (! isalnum (*c) && *c != '_') {
|
||||
return std::string ();
|
||||
} else {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the method with the given name shall fallback to NotImplemented
|
||||
*/
|
||||
static bool is_method_with_fallback (const std::string &name)
|
||||
{
|
||||
if (name == "+") {
|
||||
return true;
|
||||
} else if (name == "-") {
|
||||
return true;
|
||||
} else if (name == "/") {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
} else if (name == "*") {
|
||||
return true;
|
||||
} else if (name == "%") {
|
||||
return true;
|
||||
} else if (name == "<<") {
|
||||
return true;
|
||||
} else if (name == ">>") {
|
||||
return true;
|
||||
} else if (name == "&") {
|
||||
return true;
|
||||
} else if (name == "|") {
|
||||
return true;
|
||||
} else if (name == "^") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::add_method (const std::string &name, const gsi::MethodBase *mb)
|
||||
{
|
||||
if (is_reserved_word (name)) {
|
||||
|
||||
// drop non-standard names
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::warn << tl::to_string (tr ("Class ")) << mp_cls_decl->name () << ": " << tl::to_string (tr ("no Python mapping for method (reserved word) ")) << name;
|
||||
}
|
||||
|
||||
std::string new_name = name + "_";
|
||||
|
||||
add_method_basic (new_name, mb);
|
||||
mp_module->add_python_doc (mb, tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), new_name));
|
||||
|
||||
} else if (name == "new" && mb->ret_type ().type () == gsi::T_object && mb->ret_type ().pass_obj ()) {
|
||||
|
||||
add_method_basic (name, mb);
|
||||
|
||||
add_method_basic ("__init__", mb, true /*enabled*/, true /*constructor*/);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method is the default initializer of the object")));
|
||||
|
||||
} else if (name == "to_s" && mb->compatible_with_num_args (0)) {
|
||||
|
||||
add_method_basic (name, mb);
|
||||
|
||||
// The str method is also routed via the tp_str implementation
|
||||
add_method_basic ("__str__", mb);
|
||||
|
||||
#if defined(GSI_ALIAS_INSPECT)
|
||||
// also alias to "__repr__" unless there is an explicit "inspect" method
|
||||
bool alias_inspect = true;
|
||||
for (gsi::ClassBase::method_iterator m = mp_cls_decl->begin_methods (); m != mp_cls_decl->end_methods () && alias_inspect; ++m) {
|
||||
if (! (*m)->is_callback () && ! (*m)->is_signal ()) {
|
||||
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms () && alias_inspect; ++syn) {
|
||||
if (! syn->is_getter && ! syn->is_setter && syn->name == "inspect") {
|
||||
alias_inspect = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool alias_inspect = false;
|
||||
#endif
|
||||
if (alias_inspect) {
|
||||
add_method_basic ("__repr__", mb);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'")));
|
||||
} else {
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'str(object)'")));
|
||||
}
|
||||
|
||||
} else if (name == "hash" && mb->compatible_with_num_args (0)) {
|
||||
|
||||
// The hash method is also routed via the tp_hash implementation
|
||||
add_method_basic ("__hash__", mb);
|
||||
|
||||
add_method_basic (name, mb);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'hash(object)'")));
|
||||
|
||||
} else if (name == "inspect" && mb->compatible_with_num_args (0)) {
|
||||
|
||||
// The str method is also routed via the tp_str implementation
|
||||
add_method_basic ("__repr__", mb);
|
||||
|
||||
add_method_basic (name, mb);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'repr(object)'")));
|
||||
|
||||
} else if (name == "size" && mb->compatible_with_num_args (0)) {
|
||||
|
||||
// The size method is also routed via the sequence methods protocol if there
|
||||
// is a [] function
|
||||
add_method_basic ("__len__", mb);
|
||||
|
||||
add_method_basic (name, mb);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'len(object)'")));
|
||||
|
||||
} else if (name == "each" && mb->compatible_with_num_args (0) && mb->ret_type ().is_iter ()) {
|
||||
|
||||
// each makes the object iterable
|
||||
add_method_basic ("__iter__", mb);
|
||||
|
||||
add_method_basic (name, mb);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method enables iteration of the object")));
|
||||
|
||||
} else if (name == "dup" && mb->compatible_with_num_args (0)) {
|
||||
|
||||
// If the object supports the dup method, then it is a good
|
||||
// idea to define the __copy__ and __deepcopy__ method.
|
||||
add_method_basic ("__copy__", mb);
|
||||
add_method_basic ("__deepcopy__", mb);
|
||||
|
||||
add_method_basic (name, mb);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method also implements '__copy__' and '__deepcopy__'")));
|
||||
|
||||
} else {
|
||||
|
||||
std::string py_name = extract_python_name (name);
|
||||
if (py_name.empty ()) {
|
||||
|
||||
// drop non-standard names
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::warn << tl::to_string (tr ("Class ")) << mp_cls_decl->name () << ": " << tl::to_string (tr ("no Python mapping for method ")) << name;
|
||||
}
|
||||
|
||||
add_method_basic (name, mb, false);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method is not available for Python")));
|
||||
|
||||
} else {
|
||||
|
||||
bool fb = is_method_with_fallback (name);
|
||||
add_method_basic (py_name, mb, true, false, fb);
|
||||
|
||||
if (name == "*") {
|
||||
// Supply a commutative multiplication version unless the operator is "*!"
|
||||
add_method_basic ("__rmul__", mb);
|
||||
mp_module->add_python_doc (mb, tl::to_string (tr ("This method also implements '__rmul__'")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::add_setter (const std::string &name, const gsi::MethodBase *setter)
|
||||
{
|
||||
if (is_reserved_word (name)) {
|
||||
|
||||
std::string new_name = name + "_";
|
||||
|
||||
add_setter_basic (new_name, setter);
|
||||
mp_module->add_python_doc (setter, tl::sprintf (tl::to_string (tr ("This member is available as '%s' in Python")), new_name));
|
||||
|
||||
} else {
|
||||
add_setter_basic (name, setter);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::add_setter_basic (const std::string &name, const gsi::MethodBase *setter)
|
||||
{
|
||||
bool st = setter->is_static ();
|
||||
|
||||
std::map<std::pair<bool, std::string>, size_t>::iterator n = m_property_name_map.find (std::make_pair (st, name));
|
||||
if (n == m_property_name_map.end ()) {
|
||||
|
||||
m_property_name_map.insert (std::make_pair (std::make_pair(st, name), m_property_table.size ()));
|
||||
m_property_table.push_back (std::make_pair (MethodTableEntry (name, st, false), MethodTableEntry (name, st, false)));
|
||||
m_property_table.back ().first.add (setter);
|
||||
|
||||
} else {
|
||||
|
||||
m_property_table [n->second].first.add (setter);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::add_getter (const std::string &name, const gsi::MethodBase *getter)
|
||||
{
|
||||
if (is_reserved_word (name)) {
|
||||
|
||||
std::string new_name = name + "_";
|
||||
|
||||
add_getter_basic (new_name, getter);
|
||||
mp_module->add_python_doc (getter, tl::sprintf (tl::to_string (tr ("This member is available as '%s' in Python")), new_name));
|
||||
|
||||
} else {
|
||||
add_getter_basic (name, getter);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::add_getter_basic (const std::string &name, const gsi::MethodBase *getter)
|
||||
{
|
||||
bool st = getter->is_static ();
|
||||
|
||||
std::map<std::pair<bool, std::string>, size_t>::iterator n = m_property_name_map.find (std::make_pair (st, name));
|
||||
if (n == m_property_name_map.end ()) {
|
||||
|
||||
m_property_name_map.insert (std::make_pair (std::make_pair(st, name), m_property_table.size ()));
|
||||
m_property_table.push_back (std::make_pair (MethodTableEntry (name, st, false), MethodTableEntry (name, st, false)));
|
||||
m_property_table.back ().second.add (getter);
|
||||
|
||||
} else {
|
||||
|
||||
m_property_table [n->second].second.add (getter);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTable::is_enabled (size_t mid) const
|
||||
{
|
||||
return m_table [mid - m_method_offset].is_enabled ();
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::set_enabled (size_t mid, bool en)
|
||||
{
|
||||
m_table [mid - m_method_offset].set_enabled (en);
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTable::fallback_not_implemented (size_t mid) const
|
||||
{
|
||||
return m_table [mid - m_method_offset].fallback_not_implemented ();
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::set_fallback_not_implemented (size_t mid, bool f)
|
||||
{
|
||||
m_table [mid - m_method_offset].set_fallback_not_implemented (f);
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTable::is_init(size_t mid) const
|
||||
{
|
||||
return m_table [mid - m_method_offset].is_init ();
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::set_init (size_t mid, bool f)
|
||||
{
|
||||
m_table [mid - m_method_offset].set_init (f);
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTable::is_static (size_t mid) const
|
||||
{
|
||||
return m_table [mid - m_method_offset].is_static ();
|
||||
}
|
||||
|
||||
bool
|
||||
MethodTable::is_protected (size_t mid) const
|
||||
{
|
||||
return m_table [mid - m_method_offset].is_protected ();
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::alias (size_t mid, const std::string &new_name)
|
||||
{
|
||||
bool st = is_static (mid);
|
||||
auto nm = m_name_map.find (std::make_pair (st, new_name));
|
||||
tl_assert (nm == m_name_map.end ());
|
||||
|
||||
m_table.push_back (m_table [mid - m_method_offset]);
|
||||
m_table.back ().set_name (new_name);
|
||||
m_name_map.insert (std::make_pair (std::make_pair (st, new_name), m_table.size () - 1 - m_method_offset));
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::rename (size_t mid, const std::string &new_name)
|
||||
{
|
||||
std::string old_name = name (mid);
|
||||
bool st = is_static (mid);
|
||||
|
||||
m_table [mid - m_method_offset].set_name (new_name);
|
||||
|
||||
auto nm = m_name_map.find (std::make_pair (st, old_name));
|
||||
if (nm != m_name_map.end ()) {
|
||||
m_name_map.erase (nm);
|
||||
m_name_map.insert (std::make_pair (std::make_pair (st, new_name), mid));
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &
|
||||
MethodTable::name (size_t mid) const
|
||||
{
|
||||
return m_table [mid - m_method_offset].name ();
|
||||
}
|
||||
|
||||
const std::string &
|
||||
MethodTable::property_name (size_t mid) const
|
||||
{
|
||||
return m_property_table [mid - m_property_offset].first.name ();
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTable::begin_setters (size_t mid) const
|
||||
{
|
||||
return m_property_table[mid - m_property_offset].first.begin ();
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTable::end_setters (size_t mid) const
|
||||
{
|
||||
return m_property_table[mid - m_property_offset].first.end ();
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTable::begin_getters (size_t mid) const
|
||||
{
|
||||
return m_property_table[mid - m_property_offset].second.begin ();
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTable::end_getters (size_t mid) const
|
||||
{
|
||||
return m_property_table[mid - m_property_offset].second.end ();
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTable::begin (size_t mid) const
|
||||
{
|
||||
return m_table[mid - m_method_offset].begin ();
|
||||
}
|
||||
|
||||
MethodTableEntry::method_iterator
|
||||
MethodTable::end (size_t mid) const
|
||||
{
|
||||
return m_table[mid - m_method_offset].end ();
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::finish ()
|
||||
{
|
||||
for (std::vector<MethodTableEntry>::iterator m = m_table.begin (); m != m_table.end (); ++m) {
|
||||
m->finish ();
|
||||
if (m->is_enabled ()) {
|
||||
// disable methods which are also present as properties
|
||||
if (m_property_name_map.find (std::make_pair (m->is_static (), m->name ())) != m_property_name_map.end ()) {
|
||||
m->set_enabled (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::vector<std::pair<MethodTableEntry, MethodTableEntry> >::iterator m = m_property_table.begin (); m != m_property_table.end (); ++m) {
|
||||
m->first.finish ();
|
||||
m->second.finish ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MethodTable::add_method_basic (const std::string &name, const gsi::MethodBase *mb, bool enabled, bool init, bool fallback_not_implemented)
|
||||
{
|
||||
bool st = mb->is_static () && ! init;
|
||||
|
||||
std::map<std::pair<bool, std::string>, size_t>::iterator n = m_name_map.find (std::make_pair (st, name));
|
||||
if (n == m_name_map.end ()) {
|
||||
|
||||
m_name_map.insert (std::make_pair (std::make_pair (st, name), m_table.size ()));
|
||||
m_table.push_back (MethodTableEntry (name, st, mb->is_protected ()));
|
||||
if (! enabled) {
|
||||
m_table.back ().set_enabled (false);
|
||||
}
|
||||
if (init) {
|
||||
m_table.back ().set_init (true);
|
||||
}
|
||||
if (fallback_not_implemented) {
|
||||
m_table.back ().set_fallback_not_implemented (true);
|
||||
}
|
||||
m_table.back ().add (mb);
|
||||
|
||||
} else {
|
||||
|
||||
if (m_table [n->second].is_protected () != mb->is_protected ()) {
|
||||
tl::warn << "Class " << mp_cls_decl->name () << ": method '" << name << " is both a protected and non-protected";
|
||||
}
|
||||
|
||||
m_table [n->second].add (mb);
|
||||
if (! enabled) {
|
||||
m_table [n->second].set_enabled (false);
|
||||
}
|
||||
if (init) {
|
||||
tl_assert (m_table [n->second].is_init ());
|
||||
}
|
||||
if (fallback_not_implemented) {
|
||||
m_table.back ().set_fallback_not_implemented (true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
MethodTable *
|
||||
MethodTable::method_table_by_class (const gsi::ClassBase *cls_decl)
|
||||
{
|
||||
PythonClassClientData *cd = dynamic_cast<PythonClassClientData *>(cls_decl->data (gsi::ClientIndex::Python));
|
||||
return cd ? &cd->method_table : 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// PythonClassClientData implementation
|
||||
|
||||
PythonClassClientData::PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type, PyTypeObject *_py_type_static, PythonModule *module)
|
||||
: py_type_object ((PyObject *) _py_type), py_type_object_static ((PyObject *) _py_type_static), method_table (_cls, module)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
PyTypeObject *
|
||||
PythonClassClientData::py_type (const gsi::ClassBase &cls_decl, bool as_static)
|
||||
{
|
||||
PythonClassClientData *cd = dynamic_cast<PythonClassClientData *>(cls_decl.data (gsi::ClientIndex::Python));
|
||||
return (PyTypeObject *) (cd ? (as_static ? cd->py_type_object_static.get () : cd->py_type_object.get ()) : 0);
|
||||
}
|
||||
|
||||
void
|
||||
PythonClassClientData::initialize (const gsi::ClassBase &cls_decl, PyTypeObject *py_type, bool as_static)
|
||||
{
|
||||
PythonClassClientData *cd = dynamic_cast<PythonClassClientData *>(cls_decl.data (gsi::ClientIndex::Python));
|
||||
if (cd) {
|
||||
if (as_static) {
|
||||
cd->py_type_object_static = (PyObject *) py_type;
|
||||
} else {
|
||||
cd->py_type_object = (PyObject *) py_type;
|
||||
}
|
||||
} else {
|
||||
cls_decl.set_data (gsi::ClientIndex::Python, new PythonClassClientData (&cls_decl, as_static ? NULL : py_type, as_static ? py_type : NULL));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2022 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _HDR_pyaInternal
|
||||
#define _HDR_pyaInternal
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "pyaCommon.h"
|
||||
#include "pyaRefs.h"
|
||||
|
||||
#include "gsiClassBase.h"
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
class MethodBase;
|
||||
}
|
||||
|
||||
namespace pya
|
||||
{
|
||||
|
||||
class PythonModule;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// The lookup table for the method overload resolution
|
||||
|
||||
/**
|
||||
* @brief A single entry in the method table
|
||||
* This class provides an entry for one name. It provides flags
|
||||
* (ctor, static, protected) for the method and a list of implementations
|
||||
* (gsi::MethodBase objects)
|
||||
*/
|
||||
class MethodTableEntry
|
||||
{
|
||||
public:
|
||||
typedef std::vector<const gsi::MethodBase *>::const_iterator method_iterator;
|
||||
|
||||
MethodTableEntry (const std::string &name, bool st, bool prot);
|
||||
|
||||
const std::string &name () const;
|
||||
void set_name (const std::string &n);
|
||||
|
||||
void set_enabled (bool en);
|
||||
bool is_enabled () const;
|
||||
|
||||
void set_fallback_not_implemented (bool en);
|
||||
bool fallback_not_implemented () const;
|
||||
|
||||
void set_init(bool f);
|
||||
bool is_init () const;
|
||||
|
||||
bool is_static () const;
|
||||
bool is_protected () const;
|
||||
|
||||
void add (const gsi::MethodBase *m);
|
||||
|
||||
void finish ();
|
||||
|
||||
method_iterator begin () const;
|
||||
method_iterator end () const;
|
||||
|
||||
const std::vector<const gsi::MethodBase *> &methods () const
|
||||
{
|
||||
return m_methods;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
bool m_is_static : 1;
|
||||
bool m_is_protected : 1;
|
||||
bool m_is_enabled : 1;
|
||||
bool m_is_init : 1;
|
||||
bool m_fallback_not_implemented : 1;
|
||||
std::vector<const gsi::MethodBase *> m_methods;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The method table for a class
|
||||
* The method table will provide the methods associated with a native method, i.e.
|
||||
* a certain name. It only provides the methods, not a overload resolution strategy.
|
||||
*/
|
||||
class MethodTable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* This constructor will create a method table for the given class and register
|
||||
* this table under this class.
|
||||
*/
|
||||
MethodTable (const gsi::ClassBase *cls_decl, PythonModule *module);
|
||||
/**
|
||||
* @brief Returns the lowest method ID within the space of this table
|
||||
* Method IDs below this one are reserved for base class methods
|
||||
*/
|
||||
size_t bottom_mid () const;
|
||||
|
||||
/**
|
||||
* @brief Returns the topmost + 1 method ID.
|
||||
*/
|
||||
size_t top_mid () const;
|
||||
|
||||
/**
|
||||
* @brief Returns the lowest property method ID within the space of this table
|
||||
* Method IDs below this one are reserved for base class methods
|
||||
*/
|
||||
size_t bottom_property_mid () const;
|
||||
|
||||
/**
|
||||
* @brief Returns the topmost + 1 property method ID.
|
||||
*/
|
||||
size_t top_property_mid () const;
|
||||
|
||||
/**
|
||||
* @brief Find a method with the given name and static flag
|
||||
* Returns true or false in the first part (true, if found) and
|
||||
* the MID in the second part.
|
||||
*/
|
||||
std::pair<bool, size_t> find_method (bool st, const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Find a property with the given name and static flag
|
||||
* Returns true or false in the first part (true, if found) and
|
||||
* the MID in the second part.
|
||||
*/
|
||||
std::pair<bool, size_t> find_property (bool st, const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Adds a method to the table
|
||||
*/
|
||||
void add_method (const std::string &name, const gsi::MethodBase *mb);
|
||||
|
||||
/**
|
||||
* @brief Adds a setter with the given name
|
||||
*/
|
||||
void add_setter (const std::string &name, const gsi::MethodBase *setter);
|
||||
|
||||
/**
|
||||
* @brief Adds a getter with the given name
|
||||
*/
|
||||
void add_getter (const std::string &name, const gsi::MethodBase *getter);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the method is enabled
|
||||
*/
|
||||
bool is_enabled (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Enables or disables a method
|
||||
*/
|
||||
void set_enabled (size_t mid, bool en);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the method has a NotImplemented fallback
|
||||
*/
|
||||
bool fallback_not_implemented (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating that the method has a fallback to NotImplemented for non-matching arguments
|
||||
*/
|
||||
void set_fallback_not_implemented (size_t mid, bool f);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the method is an initializer
|
||||
*/
|
||||
bool is_init (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Sets initializer
|
||||
*/
|
||||
void set_init (size_t mid, bool f);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the method with the given ID is static
|
||||
*/
|
||||
bool is_static (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the method with the given ID is protected
|
||||
*/
|
||||
bool is_protected (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Creates an alias for the given method
|
||||
*/
|
||||
void alias (size_t mid, const std::string &new_name);
|
||||
|
||||
/**
|
||||
* @brief Renames a method
|
||||
*/
|
||||
void rename (size_t mid, const std::string &new_name);
|
||||
|
||||
/**
|
||||
* @brief Returns the name of the method with the given ID
|
||||
*/
|
||||
const std::string &name (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the name of the property with the given ID
|
||||
*/
|
||||
const std::string &property_name (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Begins iteration of the overload variants for setter of property ID mid
|
||||
*/
|
||||
MethodTableEntry::method_iterator begin_setters (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Ends iteration of the overload variants for setter of property ID mid
|
||||
*/
|
||||
MethodTableEntry::method_iterator end_setters (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Begins iteration of the overload variants for getter of property ID mid
|
||||
*/
|
||||
MethodTableEntry::method_iterator begin_getters (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Ends iteration of the overload variants for getter of property ID mid
|
||||
*/
|
||||
MethodTableEntry::method_iterator end_getters (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Begins iteration of the overload variants for method ID mid
|
||||
*/
|
||||
MethodTableEntry::method_iterator begin (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Ends iteration of the overload variants for method ID mid
|
||||
*/
|
||||
MethodTableEntry::method_iterator end (size_t mid) const;
|
||||
|
||||
/**
|
||||
* @brief Finishes construction of the table
|
||||
* This method must be called after the add_method calls have been used
|
||||
* to fill the table. It will remove duplicate entries and clean up memory.
|
||||
*/
|
||||
void finish ();
|
||||
|
||||
/**
|
||||
* @brief Obtain a method table for a given class
|
||||
*/
|
||||
static MethodTable *method_table_by_class (const gsi::ClassBase *cls_decl);
|
||||
|
||||
/**
|
||||
* @brief Gets the method table
|
||||
*/
|
||||
const std::vector<MethodTableEntry> &method_table () const
|
||||
{
|
||||
return m_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the property table
|
||||
*/
|
||||
const std::vector<std::pair<MethodTableEntry, MethodTableEntry> > &property_table () const
|
||||
{
|
||||
return m_property_table;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_method_offset;
|
||||
size_t m_property_offset;
|
||||
const gsi::ClassBase *mp_cls_decl;
|
||||
std::map<std::pair<bool, std::string>, size_t> m_name_map;
|
||||
std::map<std::pair<bool, std::string>, size_t> m_property_name_map;
|
||||
std::vector<MethodTableEntry> m_table;
|
||||
std::vector<std::pair<MethodTableEntry, MethodTableEntry> > m_property_table;
|
||||
PythonModule *mp_module;
|
||||
|
||||
void add_method_basic (const std::string &name, const gsi::MethodBase *mb, bool enabled = true, bool init = false, bool fallback_not_implemented = false);
|
||||
void add_setter_basic (const std::string &name, const gsi::MethodBase *setter);
|
||||
void add_getter_basic (const std::string &name, const gsi::MethodBase *getter);
|
||||
bool is_property_setter (bool st, const std::string &name);
|
||||
bool is_property_getter (bool st, const std::string &name);
|
||||
};
|
||||
|
||||
struct PythonClassClientData
|
||||
: public gsi::PerClassClientSpecificData
|
||||
{
|
||||
PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type, PyTypeObject *_py_type_static, PythonModule *module);
|
||||
|
||||
PythonPtr py_type_object;
|
||||
PythonPtr py_type_object_static;
|
||||
MethodTable method_table;
|
||||
|
||||
static PyTypeObject *py_type (const gsi::ClassBase &cls_decl, bool as_static);
|
||||
static void initialize (const gsi::ClassBase &cls_decl, PyTypeObject *py_type, bool as_static, PythonModule *module);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -469,7 +469,7 @@ struct writer<gsi::ObjectType>
|
|||
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
}
|
||||
|
||||
if (cls_decl->is_derived_from (atype.cls ())) {
|
||||
|
|
@ -494,14 +494,14 @@ struct writer<gsi::ObjectType>
|
|||
aa->write<void *> (new_obj);
|
||||
|
||||
} else {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), cls_decl->name ()));
|
||||
throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), cls_decl->name ()));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
}
|
||||
|
||||
if (cls_decl->is_derived_from (atype.cls ())) {
|
||||
|
|
@ -521,7 +521,7 @@ struct writer<gsi::ObjectType>
|
|||
aa->write<void *> (atype.cls ()->create_obj_from (cls_decl, p->obj ()));
|
||||
|
||||
} else {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), cls_decl->name ()));
|
||||
throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), cls_decl->name ()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class PYAObjectBase;
|
|||
* @param arg The argument to serialize (a Python object)
|
||||
* @param heap A heap for temporary objects
|
||||
*
|
||||
* The heap collects objects created while filling the buffer.
|
||||
* The heap collects objects created while filling the buffer.
|
||||
* The stack must persist as long as the serial buffer is used.
|
||||
*/
|
||||
void
|
||||
|
|
@ -84,4 +84,3 @@ void correct_constness (PyObject *obj, bool const_required);
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -45,7 +45,6 @@ namespace gsi
|
|||
namespace pya
|
||||
{
|
||||
|
||||
class PYAObjectBase;
|
||||
class Callee;
|
||||
class StatusChangedListener;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
namespace pya
|
||||
{
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Some helper macros that translate C++ exceptions into Python errors
|
||||
*/
|
||||
|
|
@ -43,6 +45,10 @@ namespace pya
|
|||
} catch (std::exception &ex) { \
|
||||
std::string msg = std::string(ex.what ()) + tl::to_string (tr (" in ")) + (where); \
|
||||
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
|
||||
} catch (tl::TypeError &ex) { \
|
||||
std::string msg; \
|
||||
msg = ex.msg () + tl::to_string (tr (" in ")) + (where); \
|
||||
PyErr_SetString (PyExc_TypeError, msg.c_str ()); \
|
||||
} catch (tl::Exception &ex) { \
|
||||
std::string msg; \
|
||||
msg = ex.msg () + tl::to_string (tr (" in ")) + (where); \
|
||||
|
|
@ -73,4 +79,3 @@ void check_error ();
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
TARGET = dbcore
|
||||
REALMODULE = db
|
||||
PYI = dbcore.pyi
|
||||
|
||||
include($$PWD/../pymod.pri)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,20 @@
|
|||
import functools
|
||||
from typing import Type
|
||||
import klayout.dbcore
|
||||
from klayout.dbcore import *
|
||||
|
||||
from klayout.db.pcell_declaration_helper import PCellDeclarationHelper
|
||||
|
||||
__all__ = klayout.dbcore.__all__ + ['PCellDeclarationHelper']
|
||||
__all__ = klayout.dbcore.__all__ + ["PCellDeclarationHelper"] # type: ignore
|
||||
|
||||
# If class has from_s, to_s, and assign, use them to
|
||||
# enable serialization.
|
||||
for name, cls in klayout.dbcore.__dict__.items():
|
||||
if not isinstance(cls, type):
|
||||
continue
|
||||
if hasattr(cls, 'from_s') and hasattr(cls, 'to_s') and hasattr(cls, 'assign'):
|
||||
cls.__getstate__ = cls.to_s # type: ignore
|
||||
def _setstate(self, str):
|
||||
cls = self.__class__
|
||||
self.assign(cls.from_s(str))
|
||||
cls.__setstate__ = _setstate # type: ignore
|
||||
|
|
|
|||
|
|
@ -63,7 +63,17 @@ class _PCellDeclarationHelper(PCellDeclaration):
|
|||
self.layer = None
|
||||
self.cell = None
|
||||
|
||||
def param(self, name, value_type, description, hidden=False, readonly=False, unit=None, default=None, choices=None):
|
||||
def param(
|
||||
self,
|
||||
name,
|
||||
value_type,
|
||||
description,
|
||||
hidden=False,
|
||||
readonly=False,
|
||||
unit=None,
|
||||
default=None,
|
||||
choices=None,
|
||||
):
|
||||
"""
|
||||
Defines a parameter
|
||||
name -> the short name of the parameter
|
||||
|
|
@ -84,11 +94,16 @@ class _PCellDeclarationHelper(PCellDeclaration):
|
|||
|
||||
# create accessor methods for the parameters
|
||||
param_index = len(self._param_decls)
|
||||
setattr(type(self), name, _PCellDeclarationHelperParameterDescriptor(param_index))
|
||||
setattr(
|
||||
type(self), name, _PCellDeclarationHelperParameterDescriptor(param_index)
|
||||
)
|
||||
|
||||
if value_type == type(self).TypeLayer:
|
||||
setattr(type(self), name + "_layer",
|
||||
_PCellDeclarationHelperLayerDescriptor(len(self._layer_param_index)))
|
||||
setattr(
|
||||
type(self),
|
||||
name + "_layer",
|
||||
_PCellDeclarationHelperLayerDescriptor(len(self._layer_param_index)),
|
||||
)
|
||||
self._layer_param_index.append(param_index)
|
||||
|
||||
# store the parameter declarations
|
||||
|
|
@ -104,10 +119,16 @@ class _PCellDeclarationHelper(PCellDeclaration):
|
|||
pdecl.unit = unit
|
||||
if not (choices is None):
|
||||
if not isinstance(choices, list) and not isinstance(choices, tuple):
|
||||
raise Exception("choices value must be an list/tuple of two-element arrays (description, value)")
|
||||
raise Exception(
|
||||
"choices value must be an list/tuple of two-element arrays (description, value)"
|
||||
)
|
||||
for c in choices:
|
||||
if (not isinstance(choices, list) and not isinstance(choices, tuple)) or len(c) != 2:
|
||||
raise Exception("choices value must be an list/tuple of two-element arrays (description, value)")
|
||||
if (
|
||||
not isinstance(choices, list) and not isinstance(choices, tuple)
|
||||
) or len(c) != 2:
|
||||
raise Exception(
|
||||
"choices value must be an list/tuple of two-element arrays (description, value)"
|
||||
)
|
||||
pdecl.add_choice(c[0], c[1])
|
||||
|
||||
# return the declaration object for further operations
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,4 @@
|
|||
from typing import Any, ClassVar, Dict, Sequence, List, Iterator, Optional
|
||||
from typing import overload
|
||||
import klayout.tl as tl
|
||||
import klayout.db as db
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
from typing import Any, ClassVar, Dict, Iterable, Optional
|
||||
from typing import Any, ClassVar, Dict, Sequence, List, Iterator, Optional
|
||||
from typing import overload
|
||||
import klayout.tl as tl
|
||||
import klayout.db as db
|
||||
class RdbReference:
|
||||
r"""
|
||||
|
|
@ -8,17 +9,32 @@ class RdbReference:
|
|||
"""
|
||||
parent_cell_id: int
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets parent cell ID for this reference
|
||||
@return The parent cell ID
|
||||
|
||||
Setter:
|
||||
@brief Sets the parent cell ID for this reference
|
||||
"""
|
||||
trans: db.DCplxTrans
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the transformation for this reference
|
||||
The transformation describes the transformation of the child cell into the parent cell. In that sense that is the usual transformation of a cell reference.
|
||||
@return The transformation
|
||||
|
||||
Setter:
|
||||
@brief Sets the transformation for this reference
|
||||
"""
|
||||
@classmethod
|
||||
def new(cls, trans: db.DCplxTrans, parent_cell_id: int) -> RdbReference:
|
||||
r"""
|
||||
@brief Creates a reference with a given transformation and parent cell ID
|
||||
"""
|
||||
def __copy__(self) -> RdbReference:
|
||||
r"""
|
||||
@brief Creates a copy of self
|
||||
"""
|
||||
def __init__(self, trans: db.DCplxTrans, parent_cell_id: int) -> None:
|
||||
r"""
|
||||
@brief Creates a reference with a given transformation and parent cell ID
|
||||
|
|
@ -64,22 +80,50 @@ class RdbReference:
|
|||
r"""
|
||||
@brief Assigns another object to self
|
||||
"""
|
||||
def create(self) -> None:
|
||||
r"""
|
||||
@brief Ensures the C++ object is created
|
||||
Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created.
|
||||
"""
|
||||
def database(self) -> ReportDatabase:
|
||||
r"""
|
||||
@brief Gets the database object that category is associated with
|
||||
|
||||
This method has been introduced in version 0.23.
|
||||
"""
|
||||
def destroy(self) -> None:
|
||||
r"""
|
||||
@brief Explicitly destroys the object
|
||||
Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception.
|
||||
If the object is not owned by the script, this method will do nothing.
|
||||
"""
|
||||
def destroyed(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the object was already destroyed
|
||||
This method returns true, if the object was destroyed, either explicitly or by the C++ side.
|
||||
The latter may happen, if the object is owned by a C++ object which got destroyed itself.
|
||||
"""
|
||||
def dup(self) -> RdbReference:
|
||||
r"""
|
||||
@brief Creates a copy of self
|
||||
"""
|
||||
def is_const_object(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the reference is a const reference
|
||||
This method returns true, if self is a const reference.
|
||||
In that case, only const methods may be called on self.
|
||||
"""
|
||||
|
||||
class RdbCell:
|
||||
r"""
|
||||
@brief A cell inside the report database
|
||||
This class represents a cell in the report database. There is not necessarily a 1:1 correspondence of RDB cells and layout database cells. Cells have an ID, a name, optionally a variant name and a set of references which describe at least one example instantiation in some parent cell. The references do not necessarily map to references or cover all references in the layout database.
|
||||
"""
|
||||
@classmethod
|
||||
def new(cls) -> RdbCell:
|
||||
r"""
|
||||
@brief Creates a new object of this class
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
r"""
|
||||
@brief Creates a new object of this class
|
||||
|
|
@ -130,22 +174,45 @@ class RdbCell:
|
|||
r"""
|
||||
@brief Removes all references from this cell
|
||||
"""
|
||||
def create(self) -> None:
|
||||
r"""
|
||||
@brief Ensures the C++ object is created
|
||||
Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created.
|
||||
"""
|
||||
def database(self) -> ReportDatabase:
|
||||
r"""
|
||||
@brief Gets the database object that category is associated with
|
||||
|
||||
This method has been introduced in version 0.23.
|
||||
"""
|
||||
def each_item(self) -> Iterable[RdbItem]:
|
||||
def destroy(self) -> None:
|
||||
r"""
|
||||
@brief Explicitly destroys the object
|
||||
Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception.
|
||||
If the object is not owned by the script, this method will do nothing.
|
||||
"""
|
||||
def destroyed(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the object was already destroyed
|
||||
This method returns true, if the object was destroyed, either explicitly or by the C++ side.
|
||||
The latter may happen, if the object is owned by a C++ object which got destroyed itself.
|
||||
"""
|
||||
def each_item(self) -> Iterator[RdbItem]:
|
||||
r"""
|
||||
@brief Iterates over all items inside the database which are associated with this cell
|
||||
|
||||
This method has been introduced in version 0.23.
|
||||
"""
|
||||
def each_reference(self) -> Iterable[RdbReference]:
|
||||
def each_reference(self) -> Iterator[RdbReference]:
|
||||
r"""
|
||||
@brief Iterates over all references
|
||||
"""
|
||||
def is_const_object(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the reference is a const reference
|
||||
This method returns true, if self is a const reference.
|
||||
In that case, only const methods may be called on self.
|
||||
"""
|
||||
def name(self) -> str:
|
||||
r"""
|
||||
@brief Gets the cell name
|
||||
|
|
@ -184,11 +251,19 @@ class RdbCategory:
|
|||
"""
|
||||
description: str
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the category description
|
||||
@return The description string
|
||||
|
||||
Setter:
|
||||
@brief Sets the category description
|
||||
@param description The description string
|
||||
"""
|
||||
@classmethod
|
||||
def new(cls) -> RdbCategory:
|
||||
r"""
|
||||
@brief Creates a new object of this class
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
r"""
|
||||
@brief Creates a new object of this class
|
||||
|
|
@ -230,22 +305,45 @@ class RdbCategory:
|
|||
|
||||
Usually it's not required to call this method. It has been introduced in version 0.24.
|
||||
"""
|
||||
def create(self) -> None:
|
||||
r"""
|
||||
@brief Ensures the C++ object is created
|
||||
Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created.
|
||||
"""
|
||||
def database(self) -> ReportDatabase:
|
||||
r"""
|
||||
@brief Gets the database object that category is associated with
|
||||
|
||||
This method has been introduced in version 0.23.
|
||||
"""
|
||||
def each_item(self) -> Iterable[RdbItem]:
|
||||
def destroy(self) -> None:
|
||||
r"""
|
||||
@brief Explicitly destroys the object
|
||||
Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception.
|
||||
If the object is not owned by the script, this method will do nothing.
|
||||
"""
|
||||
def destroyed(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the object was already destroyed
|
||||
This method returns true, if the object was destroyed, either explicitly or by the C++ side.
|
||||
The latter may happen, if the object is owned by a C++ object which got destroyed itself.
|
||||
"""
|
||||
def each_item(self) -> Iterator[RdbItem]:
|
||||
r"""
|
||||
@brief Iterates over all items inside the database which are associated with this category
|
||||
|
||||
This method has been introduced in version 0.23.
|
||||
"""
|
||||
def each_sub_category(self) -> Iterable[RdbCategory]:
|
||||
def each_sub_category(self) -> Iterator[RdbCategory]:
|
||||
r"""
|
||||
@brief Iterates over all sub-categories
|
||||
"""
|
||||
def is_const_object(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the reference is a const reference
|
||||
This method returns true, if self is a const reference.
|
||||
In that case, only const methods may be called on self.
|
||||
"""
|
||||
def name(self) -> str:
|
||||
r"""
|
||||
@brief Gets the category name
|
||||
|
|
@ -356,11 +454,14 @@ class RdbItemValue:
|
|||
"""
|
||||
tag_id: int
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the tag ID if the value is a tagged value or 0 if not
|
||||
@return The tag ID
|
||||
See \tag_id= for details about tagged values.
|
||||
|
||||
Tagged values have been added in version 0.24.
|
||||
|
||||
Setter:
|
||||
@brief Sets the tag ID to make the value a tagged value or 0 to reset it
|
||||
@param id The tag ID
|
||||
To get a tag ID, use \RdbDatabase#user_tag_id (preferred) or \RdbDatabase#tag_id (for internal use).
|
||||
|
|
@ -368,6 +469,70 @@ class RdbItemValue:
|
|||
|
||||
This variant has been introduced in version 0.24
|
||||
"""
|
||||
@classmethod
|
||||
def from_s(cls, s: str) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value object from a string
|
||||
The string format is the same than obtained by the to_s method.
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, b: db.DBox) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a DBox object
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, e: db.DEdge) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a DEdge object
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, ee: db.DEdgePair) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a DEdgePair object
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, f: float) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a numeric value
|
||||
|
||||
This variant has been introduced in version 0.24
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, p: db.DPath) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a DPath object
|
||||
|
||||
This method has been introduced in version 0.22.
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, p: db.DPolygon) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a DPolygon object
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, s: str) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a string
|
||||
"""
|
||||
@overload
|
||||
@classmethod
|
||||
def new(cls, t: db.DText) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value representing a DText object
|
||||
|
||||
This method has been introduced in version 0.22.
|
||||
"""
|
||||
def __copy__(self) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a copy of self
|
||||
"""
|
||||
@overload
|
||||
def __init__(self, b: db.DBox) -> None:
|
||||
r"""
|
||||
|
|
@ -466,6 +631,23 @@ class RdbItemValue:
|
|||
@brief Gets the box if the value represents one.
|
||||
@return The \DBox object or nil
|
||||
"""
|
||||
def create(self) -> None:
|
||||
r"""
|
||||
@brief Ensures the C++ object is created
|
||||
Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created.
|
||||
"""
|
||||
def destroy(self) -> None:
|
||||
r"""
|
||||
@brief Explicitly destroys the object
|
||||
Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception.
|
||||
If the object is not owned by the script, this method will do nothing.
|
||||
"""
|
||||
def destroyed(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the object was already destroyed
|
||||
This method returns true, if the object was destroyed, either explicitly or by the C++ side.
|
||||
The latter may happen, if the object is owned by a C++ object which got destroyed itself.
|
||||
"""
|
||||
def dup(self) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a copy of self
|
||||
|
|
@ -486,15 +668,16 @@ class RdbItemValue:
|
|||
@return The numeric value or 0
|
||||
This method has been introduced in version 0.24.
|
||||
"""
|
||||
def from_s(self, s: str) -> RdbItemValue:
|
||||
r"""
|
||||
@brief Creates a value object from a string
|
||||
The string format is the same than obtained by the to_s method.
|
||||
"""
|
||||
def is_box(self) -> bool:
|
||||
r"""
|
||||
@brief Returns true if the value object represents a box
|
||||
"""
|
||||
def is_const_object(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the reference is a const reference
|
||||
This method returns true, if self is a const reference.
|
||||
In that case, only const methods may be called on self.
|
||||
"""
|
||||
def is_edge(self) -> bool:
|
||||
r"""
|
||||
@brief Returns true if the value object represents an edge
|
||||
|
|
@ -562,13 +745,43 @@ class RdbItem:
|
|||
@brief An item inside the report database
|
||||
An item is the basic information entity in the RDB. It is associated with a cell and a category. It can be assigned values which encapsulate other objects such as strings and geometrical objects. In addition, items can be assigned an image (i.e. a screenshot image) and tags which are basically boolean flags that can be defined freely.
|
||||
"""
|
||||
@property
|
||||
def image(self) -> None:
|
||||
r"""
|
||||
WARNING: This variable can only be set, not retrieved.
|
||||
@brief Sets the attached image from a PixelBuffer object
|
||||
|
||||
This method has been added in version 0.28.
|
||||
"""
|
||||
image_str: str
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the image associated with this item as a string
|
||||
@return A base64-encoded image file (in PNG format)
|
||||
|
||||
Setter:
|
||||
@brief Sets the image from a string
|
||||
@param image A base64-encoded image file (preferably in PNG format)
|
||||
"""
|
||||
tags_str: str
|
||||
r"""
|
||||
Getter:
|
||||
@brief Returns a string listing all tags of this item
|
||||
@return A comma-separated list of tags
|
||||
|
||||
Setter:
|
||||
@brief Sets the tags from a string
|
||||
@param tags A comma-separated list of tags
|
||||
"""
|
||||
@classmethod
|
||||
def new(cls) -> RdbItem:
|
||||
r"""
|
||||
@brief Creates a new object of this class
|
||||
"""
|
||||
def __copy__(self) -> RdbItem:
|
||||
r"""
|
||||
@brief Creates a copy of self
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
r"""
|
||||
@brief Creates a new object of this class
|
||||
|
|
@ -674,6 +887,10 @@ class RdbItem:
|
|||
|
||||
This method has been introduced in version 0.25.3.
|
||||
"""
|
||||
def assign(self, other: RdbItem) -> None:
|
||||
r"""
|
||||
@brief Assigns another object to self
|
||||
"""
|
||||
def category_id(self) -> int:
|
||||
r"""
|
||||
@brief Gets the category ID
|
||||
|
|
@ -690,21 +907,61 @@ class RdbItem:
|
|||
r"""
|
||||
@brief Removes all values from this item
|
||||
"""
|
||||
def create(self) -> None:
|
||||
r"""
|
||||
@brief Ensures the C++ object is created
|
||||
Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created.
|
||||
"""
|
||||
def database(self) -> ReportDatabase:
|
||||
r"""
|
||||
@brief Gets the database object that item is associated with
|
||||
|
||||
This method has been introduced in version 0.23.
|
||||
"""
|
||||
def each_value(self) -> Iterable[RdbItemValue]:
|
||||
def destroy(self) -> None:
|
||||
r"""
|
||||
@brief Explicitly destroys the object
|
||||
Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception.
|
||||
If the object is not owned by the script, this method will do nothing.
|
||||
"""
|
||||
def destroyed(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the object was already destroyed
|
||||
This method returns true, if the object was destroyed, either explicitly or by the C++ side.
|
||||
The latter may happen, if the object is owned by a C++ object which got destroyed itself.
|
||||
"""
|
||||
def dup(self) -> RdbItem:
|
||||
r"""
|
||||
@brief Creates a copy of self
|
||||
"""
|
||||
def each_value(self) -> Iterator[RdbItemValue]:
|
||||
r"""
|
||||
@brief Iterates over all values
|
||||
"""
|
||||
def has_image(self) -> bool:
|
||||
r"""
|
||||
@brief Gets a value indicating that the item has an image attached
|
||||
See \image_str how to obtain the image.
|
||||
|
||||
This method has been introduced in version 0.28.
|
||||
"""
|
||||
def has_tag(self, tag_id: int) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the item has a tag with the given ID
|
||||
@return True, if the item has a tag with the given ID
|
||||
"""
|
||||
def image_pixels(self) -> lay.PixelBuffer:
|
||||
r"""
|
||||
@brief Gets the attached image as a PixelBuffer object
|
||||
|
||||
This method has been added in version 0.28.
|
||||
"""
|
||||
def is_const_object(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the reference is a const reference
|
||||
This method returns true, if self is a const reference.
|
||||
In that case, only const methods may be called on self.
|
||||
"""
|
||||
def is_visited(self) -> bool:
|
||||
r"""
|
||||
@brief Gets a value indicating whether the item was already visited
|
||||
|
|
@ -725,35 +982,54 @@ class ReportDatabase:
|
|||
"""
|
||||
description: str
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the databases description
|
||||
The description is a general purpose string that is supposed to further describe the database and it's content in a human-readable form.
|
||||
@return The description string
|
||||
|
||||
Setter:
|
||||
@brief Sets the databases description
|
||||
@param desc The description string
|
||||
"""
|
||||
generator: str
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the databases generator
|
||||
The generator string describes how the database was created, i.e. DRC tool name and tool options.
|
||||
In a later version this will allow re-running the tool that created the report.
|
||||
@return The generator string
|
||||
|
||||
Setter:
|
||||
@brief Sets the generator string
|
||||
@param generator The generator string
|
||||
"""
|
||||
original_file: str
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the original file name and path
|
||||
The original file name is supposed to describe the file from which this report database was generated. @return The original file name and path
|
||||
|
||||
Setter:
|
||||
@brief Sets the original file name and path
|
||||
@param path The path
|
||||
"""
|
||||
top_cell_name: str
|
||||
r"""
|
||||
Getter:
|
||||
@brief Gets the top cell name
|
||||
The top cell name identifies the top cell of the design for which the report was generated. This property must be set to establish a proper hierarchical context for a hierarchical report database. @return The top cell name
|
||||
|
||||
Setter:
|
||||
@brief Sets the top cell name string
|
||||
@param cell_name The top cell name
|
||||
"""
|
||||
@classmethod
|
||||
def new(cls, name: str) -> ReportDatabase:
|
||||
r"""
|
||||
@brief Creates a report database
|
||||
@param name The name of the database
|
||||
The name of the database will be used in the user interface to refer to a certain database.
|
||||
"""
|
||||
def __init__(self, name: str) -> None:
|
||||
r"""
|
||||
@brief Creates a report database
|
||||
|
|
@ -820,6 +1096,11 @@ class ReportDatabase:
|
|||
@param qname The qualified name of the cell (name plus variant name optionally)
|
||||
@return The cell object or nil if no such cell exists
|
||||
"""
|
||||
def create(self) -> None:
|
||||
r"""
|
||||
@brief Ensures the C++ object is created
|
||||
Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created.
|
||||
"""
|
||||
@overload
|
||||
def create_category(self, name: str) -> RdbCategory:
|
||||
r"""
|
||||
|
|
@ -892,7 +1173,7 @@ class ReportDatabase:
|
|||
@param iter The iterator (a \RecursiveShapeIterator object) from which to take the items
|
||||
"""
|
||||
@overload
|
||||
def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Iterable[db.EdgePair]) -> None:
|
||||
def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.EdgePair]) -> None:
|
||||
r"""
|
||||
@brief Creates new edge pair items for the given cell/category combination
|
||||
For each edge pair a single item will be created. The value of the item will be this edge pair.
|
||||
|
|
@ -906,7 +1187,7 @@ class ReportDatabase:
|
|||
@param edge_pairs The list of edge_pairs for which the items are created
|
||||
"""
|
||||
@overload
|
||||
def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Iterable[db.Edge]) -> None:
|
||||
def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.Edge]) -> None:
|
||||
r"""
|
||||
@brief Creates new edge items for the given cell/category combination
|
||||
For each edge a single item will be created. The value of the item will be this edge.
|
||||
|
|
@ -920,7 +1201,7 @@ class ReportDatabase:
|
|||
@param edges The list of edges for which the items are created
|
||||
"""
|
||||
@overload
|
||||
def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Iterable[db.Polygon]) -> None:
|
||||
def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.Polygon]) -> None:
|
||||
r"""
|
||||
@brief Creates new polygon items for the given cell/category combination
|
||||
For each polygon a single item will be created. The value of the item will be this polygon.
|
||||
|
|
@ -995,29 +1276,41 @@ class ReportDatabase:
|
|||
@param shapes The shape container from which to take the items
|
||||
@param trans The transformation to apply
|
||||
"""
|
||||
def each_category(self) -> Iterable[RdbCategory]:
|
||||
def destroy(self) -> None:
|
||||
r"""
|
||||
@brief Explicitly destroys the object
|
||||
Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception.
|
||||
If the object is not owned by the script, this method will do nothing.
|
||||
"""
|
||||
def destroyed(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the object was already destroyed
|
||||
This method returns true, if the object was destroyed, either explicitly or by the C++ side.
|
||||
The latter may happen, if the object is owned by a C++ object which got destroyed itself.
|
||||
"""
|
||||
def each_category(self) -> Iterator[RdbCategory]:
|
||||
r"""
|
||||
@brief Iterates over all top-level categories
|
||||
"""
|
||||
def each_cell(self) -> Iterable[RdbCell]:
|
||||
def each_cell(self) -> Iterator[RdbCell]:
|
||||
r"""
|
||||
@brief Iterates over all cells
|
||||
"""
|
||||
def each_item(self) -> Iterable[RdbItem]:
|
||||
def each_item(self) -> Iterator[RdbItem]:
|
||||
r"""
|
||||
@brief Iterates over all items inside the database
|
||||
"""
|
||||
def each_item_per_category(self, category_id: int) -> Iterable[RdbItem]:
|
||||
def each_item_per_category(self, category_id: int) -> Iterator[RdbItem]:
|
||||
r"""
|
||||
@brief Iterates over all items inside the database which are associated with the given category
|
||||
@param category_id The ID of the category for which all associated items should be retrieved
|
||||
"""
|
||||
def each_item_per_cell(self, cell_id: int) -> Iterable[RdbItem]:
|
||||
def each_item_per_cell(self, cell_id: int) -> Iterator[RdbItem]:
|
||||
r"""
|
||||
@brief Iterates over all items inside the database which are associated with the given cell
|
||||
@param cell_id The ID of the cell for which all associated items should be retrieved
|
||||
"""
|
||||
def each_item_per_cell_and_category(self, cell_id: int, category_id: int) -> Iterable[RdbItem]:
|
||||
def each_item_per_cell_and_category(self, cell_id: int, category_id: int) -> Iterator[RdbItem]:
|
||||
r"""
|
||||
@brief Iterates over all items inside the database which are associated with the given cell and category
|
||||
@param cell_id The ID of the cell for which all associated items should be retrieved
|
||||
|
|
@ -1029,6 +1322,12 @@ class ReportDatabase:
|
|||
This property is set when a database is saved or loaded. It cannot be set manually.
|
||||
@return The file name and path
|
||||
"""
|
||||
def is_const_object(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the reference is a const reference
|
||||
This method returns true, if self is a const reference.
|
||||
In that case, only const methods may be called on self.
|
||||
"""
|
||||
def is_modified(self) -> bool:
|
||||
r"""
|
||||
@brief Returns a value indicating whether the database has been modified
|
||||
|
|
@ -1129,7 +1428,7 @@ class ReportDatabase:
|
|||
|
||||
This method has been added in version 0.24.
|
||||
"""
|
||||
def variants(self, name: str) -> Iterable[int]:
|
||||
def variants(self, name: str) -> List[int]:
|
||||
r"""
|
||||
@brief Gets the variants for a given cell name
|
||||
@param name The basic name of the cell
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
TARGET = laycore
|
||||
REALMODULE = lay
|
||||
PYI = laycore.pyi
|
||||
|
||||
include($$PWD/../pymod.pri)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
TARGET = libcore
|
||||
REALMODULE = lib
|
||||
PYI = libcore.pyi
|
||||
|
||||
include($$PWD/../pymod.pri)
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,18 @@ msvc {
|
|||
}
|
||||
INSTALLS = lib_target
|
||||
|
||||
!equals(PYI, "") {
|
||||
|
||||
msvc {
|
||||
QMAKE_POST_LINK += && $(COPY) $$shell_path($$PWD/distutils_src/klayout/$$PYI) $$shell_path($$DESTDIR_PYMOD)
|
||||
} else {
|
||||
QMAKE_POST_LINK += && $(MKDIR) $$DESTDIR_PYMOD/$$REALMODULE && $(COPY) $$PWD/distutils_src/klayout/$$PYI $$DESTDIR_PYMOD
|
||||
}
|
||||
|
||||
POST_TARGETDEPS += $$PWD/distutils_src/klayout/$$PYI
|
||||
|
||||
}
|
||||
|
||||
!equals(REALMODULE, "") {
|
||||
|
||||
msvc {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
TARGET = rdbcore
|
||||
REALMODULE = rdb
|
||||
PYI = rdbcore.pyi
|
||||
|
||||
include($$PWD/../pymod.pri)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,700 +0,0 @@
|
|||
""" Stub file generation routines.
|
||||
|
||||
This module contains routines to generate stub files from klayout's python API.
|
||||
This uses the `tl` module of the API, which offers an introspection layer to
|
||||
the C-extension modules.
|
||||
"""
|
||||
|
||||
from collections import Counter
|
||||
from copy import copy
|
||||
from dataclasses import dataclass, field
|
||||
from functools import wraps
|
||||
import functools
|
||||
from sys import argv
|
||||
from textwrap import indent
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
import pya # initialize all modules
|
||||
import klayout.tl as ktl
|
||||
|
||||
|
||||
def qualified_name(_class: ktl.Class) -> str:
|
||||
name = _class.name()
|
||||
if _class.parent():
|
||||
return f"{qualified_name(_class.parent())}.{name}"
|
||||
else:
|
||||
return name
|
||||
|
||||
|
||||
def superclass(_class: ktl.Class) -> str:
|
||||
if _class.base():
|
||||
return superclass(_class.base())
|
||||
else:
|
||||
return _class.name()
|
||||
|
||||
|
||||
def is_reserved_word(name: str) -> bool:
|
||||
wordlist = [
|
||||
"and",
|
||||
"del",
|
||||
"from",
|
||||
"not",
|
||||
"while",
|
||||
"as",
|
||||
"elif",
|
||||
"global",
|
||||
"or",
|
||||
"with",
|
||||
"assert",
|
||||
"else",
|
||||
"if",
|
||||
"pass",
|
||||
"yield",
|
||||
"break",
|
||||
"except",
|
||||
"import",
|
||||
"print",
|
||||
"class",
|
||||
"exec",
|
||||
"in",
|
||||
"raise",
|
||||
"continue",
|
||||
"finally",
|
||||
"is",
|
||||
"return",
|
||||
"def",
|
||||
"for",
|
||||
"lambda",
|
||||
"try",
|
||||
"None",
|
||||
]
|
||||
return name in wordlist
|
||||
|
||||
|
||||
def translate_methodname(name: str) -> str:
|
||||
"""
|
||||
Should be the same as pyaModule.cc:extract_python_name function
|
||||
* The name string encodes some additional information, specifically:
|
||||
* "*..." The method is protected
|
||||
* "x|y" Aliases (synonyms)
|
||||
* "x|#y" y is deprecated
|
||||
* "x=" x is a setter
|
||||
* ":x" x is a getter
|
||||
* "x?" x is a predicate
|
||||
* Backslashes can be used to escape the special characters, like "*" and "|".
|
||||
"""
|
||||
if name == "new":
|
||||
new_name = "__init__"
|
||||
elif name == "++":
|
||||
new_name = "inc"
|
||||
elif name == "--":
|
||||
new_name = "dec"
|
||||
elif name == "()":
|
||||
new_name = "call"
|
||||
elif name == "!":
|
||||
new_name = "not"
|
||||
elif name == "==":
|
||||
new_name = "__eq__"
|
||||
elif name == "!=":
|
||||
new_name = "__ne__"
|
||||
elif name == "<":
|
||||
new_name = "__lt__"
|
||||
elif name == "<=":
|
||||
new_name = "__le__"
|
||||
elif name == ">":
|
||||
new_name = "__gt__"
|
||||
elif name == ">=":
|
||||
new_name = "__ge__"
|
||||
elif name == "<=>":
|
||||
new_name = "__cmp__"
|
||||
elif name == "+":
|
||||
new_name = "__add__"
|
||||
elif name == "+@":
|
||||
new_name = "__pos__"
|
||||
elif name == "-":
|
||||
new_name = "__sub__"
|
||||
elif name == "-@":
|
||||
new_name = "__neg__"
|
||||
elif name == "/":
|
||||
new_name = "__truediv__"
|
||||
elif name == "*":
|
||||
new_name = "__mul__"
|
||||
elif name == "%":
|
||||
new_name = "__mod__"
|
||||
elif name == "<<":
|
||||
new_name = "__lshift__"
|
||||
elif name == ">>":
|
||||
new_name = "__rshift__"
|
||||
elif name == "~":
|
||||
new_name = "__invert__"
|
||||
elif name == "&":
|
||||
new_name = "__and__"
|
||||
elif name == "|":
|
||||
new_name = "__or__"
|
||||
elif name == "^":
|
||||
new_name = "__xor__"
|
||||
elif name == "+=":
|
||||
new_name = "__iadd__"
|
||||
elif name == "-=":
|
||||
new_name = "__isub__"
|
||||
elif name == "/=":
|
||||
new_name = "__itruediv__"
|
||||
elif name == "*=":
|
||||
new_name = "__imul__"
|
||||
elif name == "%=":
|
||||
new_name = "__imod__"
|
||||
elif name == "<<=":
|
||||
new_name = "__ilshift__"
|
||||
elif name == ">>=":
|
||||
new_name = "__irshift__"
|
||||
elif name == "&=":
|
||||
new_name = "__iand__"
|
||||
elif name == "|=":
|
||||
new_name = "__ior__"
|
||||
elif name == "^=":
|
||||
new_name = "__ixor__"
|
||||
elif name == "[]":
|
||||
new_name = "__getitem__"
|
||||
elif name == "[]=":
|
||||
new_name = "__setitem__"
|
||||
else:
|
||||
# Ignore other conversions for now.
|
||||
if name.startswith("*"):
|
||||
print(name)
|
||||
new_name = name
|
||||
if is_reserved_word(new_name):
|
||||
new_name = new_name + "_"
|
||||
|
||||
return new_name
|
||||
|
||||
|
||||
_type_dict = dict()
|
||||
|
||||
_type_dict[ktl.ArgType.TypeBool] = "bool"
|
||||
_type_dict[ktl.ArgType.TypeChar] = "str"
|
||||
_type_dict[ktl.ArgType.TypeDouble] = "float"
|
||||
_type_dict[ktl.ArgType.TypeFloat] = "float"
|
||||
_type_dict[ktl.ArgType.TypeInt] = "int"
|
||||
_type_dict[ktl.ArgType.TypeLong] = "int"
|
||||
_type_dict[ktl.ArgType.TypeLongLong] = "int"
|
||||
# _type_dict[ktl.ArgType.TypeMap] = None
|
||||
# _type_dict[ktl.ArgType.TypeObject] = None
|
||||
_type_dict[ktl.ArgType.TypeSChar] = "str"
|
||||
_type_dict[ktl.ArgType.TypeShort] = "int"
|
||||
_type_dict[ktl.ArgType.TypeString] = "str"
|
||||
_type_dict[ktl.ArgType.TypeUChar] = "str"
|
||||
_type_dict[ktl.ArgType.TypeUInt] = "int"
|
||||
_type_dict[ktl.ArgType.TypeULong] = "int"
|
||||
_type_dict[ktl.ArgType.TypeULongLong] = "int"
|
||||
_type_dict[ktl.ArgType.TypeUShort] = "int"
|
||||
_type_dict[ktl.ArgType.TypeVar] = "Any"
|
||||
# _type_dict[ktl.ArgType.TypeVector] = None
|
||||
_type_dict[ktl.ArgType.TypeVoid] = "None"
|
||||
_type_dict[ktl.ArgType.TypeVoidPtr] = "None"
|
||||
|
||||
|
||||
def _translate_type(arg_type: ktl.ArgType, within_class: ktl.Class) -> str:
|
||||
"""Translates klayout's C-type to a type in Python.
|
||||
|
||||
This function is equivalent to the `type_to_s` in `pyaModule.cc`.
|
||||
See also `type_to_s` in `layGSIHelpProvider.cc`"""
|
||||
|
||||
py_str: str = ""
|
||||
if arg_type.type() == ktl.ArgType.TypeObject:
|
||||
if within_class.module() and arg_type.cls().module() != within_class.module():
|
||||
py_str = arg_type.cls().module() + "." + qualified_name(arg_type.cls())
|
||||
else:
|
||||
py_str = qualified_name(arg_type.cls())
|
||||
elif arg_type.type() == ktl.ArgType.TypeMap:
|
||||
inner_key = _translate_type(arg_type.inner_k(), within_class)
|
||||
inner_val = _translate_type(arg_type.inner(), within_class)
|
||||
py_str = f"Dict[{inner_key}, {inner_val}]"
|
||||
elif arg_type.type() == ktl.ArgType.TypeVector:
|
||||
py_str = f"Iterable[{_translate_type(arg_type.inner(), within_class)}]"
|
||||
else:
|
||||
py_str = _type_dict[arg_type.type()]
|
||||
|
||||
if arg_type.is_iter():
|
||||
py_str = f"Iterable[{py_str}]"
|
||||
if arg_type.has_default():
|
||||
py_str = f"Optional[{py_str}] = ..."
|
||||
return py_str
|
||||
|
||||
|
||||
@dataclass
|
||||
class _Method:
|
||||
name: str
|
||||
is_setter: bool
|
||||
is_getter: bool
|
||||
is_classvar: bool
|
||||
is_classmethod: bool
|
||||
doc: str
|
||||
m: ktl.Method
|
||||
|
||||
|
||||
@dataclass
|
||||
class Stub:
|
||||
signature: str
|
||||
name: Any
|
||||
docstring: str
|
||||
indent_docstring: bool = True
|
||||
child_stubs: List["Stub"] = field(default_factory=list)
|
||||
decorator: str = ""
|
||||
|
||||
def __eq__(self, __o: object) -> bool:
|
||||
if not isinstance(__o, Stub):
|
||||
return False
|
||||
return (
|
||||
self.signature == __o.signature
|
||||
and self.child_stubs == __o.child_stubs
|
||||
and self.decorator == __o.decorator
|
||||
)
|
||||
|
||||
def __lt__(self, other: "Stub") -> bool:
|
||||
# mypy complains if an overload with float happens before int
|
||||
self_signature = self.signature.replace(": int", "0").replace(": float", "1")
|
||||
other_signature = other.signature.replace(": int", "0").replace(": float", "1")
|
||||
|
||||
self_sortkey = self.name, len(self.signature.split(",")), self_signature
|
||||
other_sortkey = other.name, len(other.signature.split(",")), other_signature
|
||||
return self_sortkey < other_sortkey
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.format_stub(include_docstring=False))
|
||||
|
||||
def format_stub(self, include_docstring=True):
|
||||
lines = []
|
||||
lines.extend(self.decorator.splitlines())
|
||||
if self.indent_docstring: # all but properties
|
||||
if include_docstring or len(self.child_stubs) > 0:
|
||||
lines.append(self.signature + ":")
|
||||
else:
|
||||
lines.append(self.signature + ": ...")
|
||||
else:
|
||||
lines.append(self.signature)
|
||||
|
||||
stub_str = "\n".join(lines)
|
||||
|
||||
lines = []
|
||||
lines.append('r"""')
|
||||
lines.extend(self.docstring.splitlines())
|
||||
lines.append('"""')
|
||||
doc_str = "\n".join(lines)
|
||||
|
||||
# indent only if it is required (methods, not properties)
|
||||
if self.indent_docstring:
|
||||
doc_str = indent(doc_str, " " * 4)
|
||||
|
||||
if include_docstring:
|
||||
stub_str += "\n"
|
||||
stub_str += doc_str
|
||||
|
||||
for stub in self.child_stubs:
|
||||
stub_str += "\n"
|
||||
stub_str += indent(
|
||||
stub.format_stub(include_docstring=include_docstring), " " * 4
|
||||
)
|
||||
|
||||
return stub_str
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class MethodStub(Stub):
|
||||
indent_docstring: bool = True
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class PropertyStub(Stub):
|
||||
indent_docstring: bool = False
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class ClassStub(Stub):
|
||||
indent_docstring: bool = True
|
||||
|
||||
|
||||
def get_c_methods(c: ktl.Class) -> List[_Method]:
|
||||
"""
|
||||
Iterates over all methods defined in the C API, sorting
|
||||
properties, class methods and bound methods.
|
||||
"""
|
||||
method_list: List[_Method] = list()
|
||||
setters = set()
|
||||
|
||||
def primary_synonym(m: ktl.Method) -> ktl.MethodOverload:
|
||||
for ms in m.each_overload():
|
||||
if ms.name() == m.primary_name():
|
||||
return ms
|
||||
raise ("Primary synonym not found for method " + m.name())
|
||||
|
||||
for m in c.each_method():
|
||||
if m.is_signal():
|
||||
# ignore signals as they do not have arguments and are neither setters nor getters.
|
||||
continue
|
||||
|
||||
method_def = primary_synonym(m)
|
||||
if method_def.is_setter():
|
||||
setters.add(method_def.name())
|
||||
|
||||
for m in c.each_method():
|
||||
num_args = len([True for a in m.each_argument()])
|
||||
method_def = primary_synonym(m)
|
||||
|
||||
# extended definition of "getter" for Python
|
||||
is_getter = (num_args == 0) and (
|
||||
method_def.is_getter()
|
||||
or (not method_def.is_setter() and method_def.name() in setters)
|
||||
)
|
||||
is_setter = (num_args == 1) and method_def.is_setter()
|
||||
is_classvar = (num_args == 0) and (m.is_static() and not m.is_constructor())
|
||||
method_list.append(
|
||||
_Method(
|
||||
name=method_def.name(),
|
||||
is_setter=is_setter,
|
||||
is_getter=is_getter or is_classvar,
|
||||
is_classmethod=m.is_constructor(),
|
||||
is_classvar=is_classvar,
|
||||
doc=m.doc(),
|
||||
m=m,
|
||||
)
|
||||
)
|
||||
|
||||
# print(f"{m.name()}: {m.is_static()=}, {m.is_constructor()=}, {m.is_const_object()=}")
|
||||
return method_list
|
||||
|
||||
|
||||
def get_py_child_classes(c: ktl.Class):
|
||||
for c_child in c.each_child_class():
|
||||
return c_child
|
||||
|
||||
|
||||
def get_py_methods(
|
||||
c: ktl.Class,
|
||||
) -> List[Stub]:
|
||||
c_methods = get_c_methods(c)
|
||||
|
||||
# extract properties
|
||||
_c_methods = copy(c_methods)
|
||||
|
||||
# Helper functions
|
||||
def find_setter(c_methods: List[_Method], name: str):
|
||||
"""Finds a setter method in c_methods list with a given name.f"""
|
||||
for m in c_methods:
|
||||
if m.name == name and m.is_setter:
|
||||
return m
|
||||
return None
|
||||
|
||||
def find_getter(c_methods: List[_Method], name: str):
|
||||
"""Finds a getter method in c_methods list with a given name.f"""
|
||||
for m in c_methods:
|
||||
if m.name == name and m.is_getter:
|
||||
return m
|
||||
return None
|
||||
|
||||
translate_type = functools.partial(_translate_type, within_class=c)
|
||||
|
||||
def _get_arglist(m: ktl.Method, self_str) -> List[Tuple[str, ktl.ArgType]]:
|
||||
args = [(self_str, None)]
|
||||
for i, a in enumerate(m.each_argument()):
|
||||
argname = a.name()
|
||||
if is_reserved_word(argname):
|
||||
argname += "_"
|
||||
elif not argname:
|
||||
argname = f"arg{i}"
|
||||
args.append((argname, a))
|
||||
return args
|
||||
|
||||
def _format_args(arglist: List[Tuple[str, Optional[str]]]):
|
||||
args = []
|
||||
for argname, argtype in arglist:
|
||||
if argtype:
|
||||
args.append(f"{argname}: {argtype}")
|
||||
else:
|
||||
args.append(argname)
|
||||
return ", ".join(args)
|
||||
|
||||
def format_args(m: ktl.Method, self_str: str = "self") -> str:
|
||||
arg_list = _get_arglist(m, self_str=self_str)
|
||||
new_arglist: List[Tuple[str, Optional[str]]] = []
|
||||
for argname, a in arg_list:
|
||||
if a:
|
||||
new_arglist.append((argname, translate_type(a)))
|
||||
else:
|
||||
new_arglist.append((argname, None))
|
||||
return _format_args(new_arglist)
|
||||
|
||||
# Extract all properties (methods that have getters and/or setters)
|
||||
properties: List[Stub] = list()
|
||||
for m in copy(_c_methods):
|
||||
ret_type = translate_type(m.m.ret_type())
|
||||
if m.is_getter:
|
||||
m_setter = find_setter(c_methods, m.name)
|
||||
if m_setter is not None: # full property
|
||||
doc = m.doc + m_setter.doc
|
||||
properties.append(
|
||||
PropertyStub(
|
||||
decorator="",
|
||||
signature=f"{translate_methodname(m.name)}: {ret_type}",
|
||||
name=f"{translate_methodname(m.name)}",
|
||||
docstring=doc,
|
||||
)
|
||||
)
|
||||
# _c_methods.remove(m_setter)
|
||||
elif m.is_classvar:
|
||||
properties.append(
|
||||
PropertyStub(
|
||||
decorator="",
|
||||
signature=f"{translate_methodname(m.name)}: ClassVar[{ret_type}]",
|
||||
name=f"{translate_methodname(m.name)}",
|
||||
docstring=m.doc,
|
||||
)
|
||||
)
|
||||
else: # only getter
|
||||
properties.append(
|
||||
MethodStub(
|
||||
decorator="@property",
|
||||
signature=f"def {translate_methodname(m.name)}(self) -> {ret_type}",
|
||||
name=f"{translate_methodname(m.name)}",
|
||||
docstring=m.doc,
|
||||
)
|
||||
)
|
||||
_c_methods.remove(m)
|
||||
elif m.is_setter and not find_getter(
|
||||
c_methods, m.name
|
||||
): # include setter-only properties as full properties
|
||||
doc = "WARNING: This variable can only be set, not retrieved.\n" + m.doc
|
||||
properties.append(
|
||||
PropertyStub(
|
||||
decorator="",
|
||||
signature=f"{translate_methodname(m.name)}: {ret_type}",
|
||||
name=f"{translate_methodname(m.name)}",
|
||||
docstring=doc,
|
||||
)
|
||||
)
|
||||
_c_methods.remove(m)
|
||||
|
||||
for m in copy(_c_methods):
|
||||
if m.is_setter:
|
||||
_c_methods.remove(m)
|
||||
|
||||
def get_altnames(c_name: str):
|
||||
names = [c_name]
|
||||
if c_name == "to_s":
|
||||
names.append("__str__")
|
||||
return names
|
||||
|
||||
# Extract all classmethods
|
||||
classmethods: List[Stub] = list()
|
||||
for m in copy(_c_methods):
|
||||
if m.is_classmethod:
|
||||
# Exception: if it is an __init__ constructor, ignore.
|
||||
# Will be treated by the bound method logic below.
|
||||
if translate_methodname(m.name) == "__init__":
|
||||
continue
|
||||
decorator = "@classmethod"
|
||||
ret_type = translate_type(m.m.ret_type())
|
||||
for name in get_altnames(m.name):
|
||||
classmethods.append(
|
||||
MethodStub(
|
||||
decorator=decorator,
|
||||
signature=f"def {translate_methodname(name)}({format_args(m.m, 'cls')}) -> {ret_type}",
|
||||
name=f"{translate_methodname(name)}",
|
||||
docstring=m.doc,
|
||||
)
|
||||
)
|
||||
_c_methods.remove(m)
|
||||
|
||||
# Extract bound methods
|
||||
boundmethods: List[Stub] = list()
|
||||
for m in copy(_c_methods):
|
||||
decorator = ""
|
||||
|
||||
translated_name = translate_methodname(m.name)
|
||||
if translated_name in [s.name for s in properties]:
|
||||
translated_name += "_"
|
||||
|
||||
if translated_name == "__init__":
|
||||
ret_type = "None"
|
||||
else:
|
||||
ret_type = translate_type(m.m.ret_type())
|
||||
|
||||
arg_list = _get_arglist(m.m, "self")
|
||||
# Exceptions:
|
||||
# For X.__eq__(self, a:X), treat second argument as type object instead of X
|
||||
if translated_name in ("__eq__", "__ne__"):
|
||||
arg_list[1] = arg_list[1][0], "object"
|
||||
# X._assign(self, other:X), mypy complains if _assign is defined at base class.
|
||||
# We can't specialize other in this case.
|
||||
elif translated_name in ("_assign", "assign"):
|
||||
assert arg_list[1][1].type() == ktl.ArgType.TypeObject
|
||||
arg_list[1] = arg_list[1][0], superclass(arg_list[1][1].cls())
|
||||
else:
|
||||
new_arg_list = []
|
||||
for argname, a in arg_list:
|
||||
if a:
|
||||
new_arg_list.append((argname, translate_type(a)))
|
||||
else:
|
||||
new_arg_list.append((argname, a))
|
||||
arg_list = new_arg_list
|
||||
formatted_args = _format_args(arg_list)
|
||||
|
||||
for name in get_altnames(translated_name):
|
||||
boundmethods.append(
|
||||
MethodStub(
|
||||
decorator=decorator,
|
||||
signature=f"def {name}({formatted_args}) -> {ret_type}",
|
||||
name=f"{name}",
|
||||
docstring=m.doc,
|
||||
)
|
||||
)
|
||||
_c_methods.remove(m)
|
||||
|
||||
def add_overload_decorator(stublist: List[Stub]):
|
||||
stubnames = [stub.name for stub in stublist]
|
||||
for stub in stublist:
|
||||
has_duplicate = stubnames.count(stub.name) > 1
|
||||
if has_duplicate:
|
||||
stub.decorator = "@overload\n" + stub.decorator
|
||||
return stublist
|
||||
|
||||
boundmethods = sorted(set(boundmethods)) # sometimes duplicate methods are defined.
|
||||
add_overload_decorator(boundmethods)
|
||||
properties = sorted(set(properties))
|
||||
classmethods = sorted(classmethods)
|
||||
add_overload_decorator(classmethods)
|
||||
|
||||
return_list: List[Stub] = properties + classmethods + boundmethods
|
||||
|
||||
return return_list
|
||||
|
||||
|
||||
def get_class_stub(
|
||||
c: ktl.Class,
|
||||
ignore: List[ktl.Class] = None,
|
||||
module: str = "",
|
||||
) -> ClassStub:
|
||||
base = ""
|
||||
if c.base():
|
||||
base = f"({c.base().name()})"
|
||||
if c.module() != module:
|
||||
full_name = c.module() + "." + c.name()
|
||||
else:
|
||||
full_name = c.name()
|
||||
_cstub = ClassStub(
|
||||
signature="class " + full_name + base, docstring=c.doc(), name=full_name
|
||||
)
|
||||
child_attributes = get_py_methods(c)
|
||||
for child_c in c.each_child_class():
|
||||
_cstub.child_stubs.append(
|
||||
get_class_stub(
|
||||
child_c,
|
||||
ignore=ignore,
|
||||
module=c.module(),
|
||||
)
|
||||
)
|
||||
for stub in child_attributes:
|
||||
_cstub.child_stubs.append(stub)
|
||||
return _cstub
|
||||
|
||||
def get_classes(module: str) -> List[ktl.Class]:
|
||||
_classes = []
|
||||
for c in ktl.Class.each_class():
|
||||
if c.module() != module:
|
||||
continue
|
||||
_classes.append(c)
|
||||
return _classes
|
||||
|
||||
def get_module_stubs(module:str) -> List[ClassStub]:
|
||||
_stubs = []
|
||||
_classes = get_classes(module)
|
||||
for c in _classes:
|
||||
_cstub = get_class_stub(c, ignore=_classes, module=module)
|
||||
_stubs.append(_cstub)
|
||||
return _stubs
|
||||
|
||||
|
||||
def print_db():
|
||||
print("from typing import Any, ClassVar, Dict, Iterable, Optional")
|
||||
print("from typing import overload")
|
||||
print("import klayout.rdb as rdb")
|
||||
print("import klayout.tl as tl")
|
||||
for stub in get_module_stubs("db"):
|
||||
print(stub.format_stub(include_docstring=True) + "\n")
|
||||
|
||||
|
||||
def print_rdb():
|
||||
print("from typing import Any, ClassVar, Dict, Iterable, Optional")
|
||||
print("from typing import overload")
|
||||
print("import klayout.db as db")
|
||||
for stub in get_module_stubs("rdb"):
|
||||
print(stub.format_stub(include_docstring=True) + "\n")
|
||||
|
||||
def print_tl():
|
||||
print("from typing import Any, ClassVar, Dict, Iterable, Optional")
|
||||
print("from typing import overload")
|
||||
for stub in get_module_stubs("tl"):
|
||||
print(stub.format_stub(include_docstring=True) + "\n")
|
||||
|
||||
|
||||
def test_v1():
|
||||
db_classes = get_classes("db")
|
||||
for c in db_classes:
|
||||
if c.name() != "Region":
|
||||
continue
|
||||
print(
|
||||
get_class_stub(c, ignore=db_classes, module="db").format_stub(
|
||||
include_docstring=False
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_v2():
|
||||
db_classes = get_classes("db")
|
||||
for c in db_classes:
|
||||
if c.name() != "DPoint":
|
||||
continue
|
||||
print(
|
||||
get_class_stub(c, ignore=db_classes, module="db").format_stub(
|
||||
include_docstring=True
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_v3():
|
||||
db_classes = get_classes("db")
|
||||
for c in db_classes:
|
||||
if c.name() != "Instance":
|
||||
continue
|
||||
print(
|
||||
get_class_stub(c, ignore=db_classes, module="db").format_stub(
|
||||
include_docstring=False
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_v4():
|
||||
db_classes = get_classes("db")
|
||||
for c in db_classes:
|
||||
if c.name() != "Region":
|
||||
continue
|
||||
for cclass in get_py_child_classes(c):
|
||||
print(
|
||||
get_class_stub(cclass, ignore=db_classes, module="db").format_stub(
|
||||
include_docstring=False
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) < 2:
|
||||
print("Specity module in argument: 'db', 'rdb', 'tl'")
|
||||
exit(1)
|
||||
if argv[1] == "db":
|
||||
print_db()
|
||||
elif argv[1] == "rdb":
|
||||
print_rdb()
|
||||
elif argv[1] == "tl":
|
||||
print_tl()
|
||||
else:
|
||||
# print_rdb()
|
||||
# test_v4()
|
||||
print("Wrong arguments")
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
TARGET = tlcore
|
||||
REALMODULE = tl
|
||||
PYI = tlcore.pyi
|
||||
|
||||
include($$PWD/../pymod.pri)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
Author: Thomas Ferreira de Lima
|
||||
email: thomas@tlima.me
|
||||
|
||||
## Notes
|
||||
To use the stubgen script for the three main modules, run the following from the root folder of the repository:
|
||||
`$ python ./src/pymod/stubgen.py db >! src/pymod/distutils_src/klayout/dbcore.pyi`
|
||||
`$ python ./src/pymod/stubgen.py rdb >! src/pymod/distutils_src/klayout/rdbcore.pyi`
|
||||
`$ python ./src/pymod/stubgen.py tl >! src/pymod/distutils_src/klayout/tlcore.pyi`
|
||||
|
||||
To compare the generated stubs with a python self-inspection of the klayout module, try the following:
|
||||
Navigate to `./src/pymod/distutils_src`.
|
||||
Run, for example:
|
||||
`$ stubtest klayout.tlcore`
|
||||
|
||||
TODO:
|
||||
- [ ] Integrate above scripts with CI
|
||||
## Old notes
|
||||
CHECKLIST:
|
||||
- [x] 1. Use klayout.tl to inspect all classes and methods in pya.
|
||||
- [x] 2. Figure out last few bugs.
|
||||
- DPoint has a method with "=" when it should have been "*=". There must be an issue with the gsiDeclInternal algorithms.
|
||||
- Some inner classes, e.g. LogicalOp inside CompoundRegionOperationNode are not returning
|
||||
- [x] 3. Manually check and compare to mypy's output.
|
||||
- Looks good, but there are a few discrepancies between actual python module and stubs. Namely, deprecated methods were not included in the stub. The opposite is sometimes true as well, though for newer, experimental classes e.g. `klayout.dbcore.GenericDeviceCombiner.combine_devices`.
|
||||
|
|
@ -1556,6 +1556,17 @@ rba_add_path (const std::string &path)
|
|||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
ruby_name (const std::string &n)
|
||||
{
|
||||
if (n == "*!") {
|
||||
// non-commutative multiplication
|
||||
return "*";
|
||||
} else {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
|
@ -1672,7 +1683,7 @@ public:
|
|||
} else if (syn->is_setter) {
|
||||
mt->add_method (syn->name + "=", *m);
|
||||
} else {
|
||||
mt->add_method (syn->name, *m);
|
||||
mt->add_method (ruby_name (syn->name), *m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1709,7 +1720,7 @@ public:
|
|||
|
||||
} else {
|
||||
|
||||
mt->add_method (syn->name, *m);
|
||||
mt->add_method (ruby_name (syn->name), *m);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,13 +36,13 @@ namespace tl
|
|||
/**
|
||||
* @brief The unspecific exception class
|
||||
*
|
||||
* This class is the base class for all exceptions in this
|
||||
* framework. It does not carry further information except
|
||||
* a message string that can be created through different
|
||||
* This class is the base class for all exceptions in this
|
||||
* framework. It does not carry further information except
|
||||
* a message string that can be created through different
|
||||
* constructor methods.
|
||||
*/
|
||||
|
||||
class TL_PUBLIC Exception
|
||||
class TL_PUBLIC Exception
|
||||
{
|
||||
public:
|
||||
Exception (const std::string &msg)
|
||||
|
|
@ -169,6 +169,15 @@ private:
|
|||
void init (const std::string &fmt, const std::vector<tl::Variant> &a);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An exception thrown when the wrong type is provided as argument.
|
||||
*/
|
||||
struct TL_PUBLIC TypeError
|
||||
: public Exception
|
||||
{
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A "neutral" exception thrown to terminate some operation
|
||||
* This exception is not shown.
|
||||
|
|
@ -195,4 +204,3 @@ struct TL_PUBLIC InternalException
|
|||
} // namespace tl
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -48,5 +48,3 @@ if __name__ == '__main__':
|
|||
|
||||
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import unittest
|
|||
import os
|
||||
import sys
|
||||
import gc
|
||||
import copy
|
||||
|
||||
# Set this to True to disable some tests involving exceptions
|
||||
leak_check = "TEST_LEAK_CHECK" in os.environ
|
||||
|
|
@ -3073,6 +3074,36 @@ class BasicTest(unittest.TestCase):
|
|||
go = None
|
||||
self.assertEqual(pya.GObject.g_inst_count(), gc)
|
||||
|
||||
# fallback to __rmul__ for not implemented __mul__
|
||||
|
||||
def test_90(self):
|
||||
class RMulObject:
|
||||
def __init__(self, factor):
|
||||
self.factor = factor
|
||||
def __rmul__(self, point):
|
||||
return point * self.factor
|
||||
def __radd__(self, point):
|
||||
return point + pya.Vector(1,1) * self.factor
|
||||
|
||||
p = pya.Point(1, 0)
|
||||
fac2 = RMulObject(2)
|
||||
self.assertEqual(p * 2, p * fac2) # p.__mul__(fac2) should return NotImplemented, which will call fac2.__rmul__(p)
|
||||
self.assertEqual(pya.Point(3,2), p + fac2)
|
||||
|
||||
# copy and deepcopy
|
||||
|
||||
def test_91(self):
|
||||
|
||||
p = pya.Point(1, 0)
|
||||
pc = copy.copy(p)
|
||||
pdc = copy.deepcopy(p)
|
||||
|
||||
pdc.x = 4
|
||||
pc.x = 3
|
||||
p.x = 2
|
||||
self.assertEqual(p.x, 2)
|
||||
self.assertEqual(pc.x, 3)
|
||||
self.assertEqual(pdc.x, 4)
|
||||
|
||||
# run unit tests
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
Loading…
Reference in New Issue