From 533e7f541fe887ac5061610ab3710113667f5e6b Mon Sep 17 00:00:00 2001 From: Alex Tait Date: Fri, 14 Jun 2019 21:23:16 -0600 Subject: [PATCH] fixing autolibrary in environments where klayout (i.e. pya) is not installed --- lygadgets/autolibrary.py | 187 ++++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 89 deletions(-) diff --git a/lygadgets/autolibrary.py b/lygadgets/autolibrary.py index fe6af40..b36e846 100644 --- a/lygadgets/autolibrary.py +++ b/lygadgets/autolibrary.py @@ -62,100 +62,109 @@ def my_argspec(function): return args, kwargs -class WrappedPCell(pya.PCellDeclarationHelper): - ''' Wraps a non-klayout PCell as a klayout PCell. - The pcell is here defined as a function with arguments. - When produce_impl is called, the cell geometry is compiled and added to this instance's cell. - - The transfer of external pcell geometry is done through a temporary gds file. - - I think this is not specific to phidl implementation. It just needs some function. - Oh wait, yes it is (barely) because of write_gds. - ''' - generating_function = None - - def kwargs_to_params(self): - ''' Extracts the arguments of the generating_function and registers them as params of this object - If it is a keyword argument, the default type is detected and value added as default - ''' - def kwarg_to_param(key, default=None): - self.param(key, pytype_to_PCelltype(type(default)), key, default=default) - args, kwargs = my_argspec(self.generating_function) - for arg in args: - kwarg_to_param(arg) - for key, default in kwargs.items(): - kwarg_to_param(key, default) - - def params_to_kwargs(self): - ''' Inverse of kwargs_to_params. Converts the parameters of this instance and their chosen values - back into arguments that can be sent to the generating_function. - ''' - # todo: handle non-keyword args - all_args = list() - all_kwargs = dict() - for pdecl, pval in zip(self.get_parameters(), self.get_values()): - all_kwargs[pdecl.name] = pval - return all_args, all_kwargs - - def __init__(self, generating_function): - self.generating_function = generating_function - super().__init__() - self.kwargs_to_params() - - def display_text_impl(self): - ''' Produces a string that includes all of the parameters. This means it is unique for identical cells. - We assume that the pcells are functional in the sense that identical parameters yield identical geometry. - In this way, different pcell instances with the same arguments are correctly identified as the same cell. +# protect against missing pya +if pya is None: + class WrappedPCell(object): + def __init__(self, *args, **kwargs): + raise AttributeError('WrappedPCell requires a working installation of klayout python package') + class WrappedLibrary(object): + def __init__(self, *args, **kwargs): + raise AttributeError('WrappedLibrary requires a working installation of klayout python package') +else: + class WrappedPCell(pya.PCellDeclarationHelper): + ''' Wraps a non-klayout PCell as a klayout PCell. + The pcell is here defined as a function with arguments. + When produce_impl is called, the cell geometry is compiled and added to this instance's cell. + + The transfer of external pcell geometry is done through a temporary gds file. + + I think this is not specific to phidl implementation. It just needs some function. + Oh wait, yes it is (barely) because of write_gds. ''' - text = self.generating_function.__name__ + '_' - for pdecl, pval in zip(self.get_parameters(), self.get_values()): - # sanitize _'s and ='s - if isinstance(pval, str): - pval.replace('_', '[_]') - pval.replace('=', '[=]') - text += '_{}={}'.format(pdecl.name, pval) - return text - - def produce_impl(self): - ''' Creates a fixed cell instance based on the previously specified parameters + generating_function = None + + def kwargs_to_params(self): + ''' Extracts the arguments of the generating_function and registers them as params of this object + If it is a keyword argument, the default type is detected and value added as default + ''' + def kwarg_to_param(key, default=None): + self.param(key, pytype_to_PCelltype(type(default)), key, default=default) + args, kwargs = my_argspec(self.generating_function) + for arg in args: + kwarg_to_param(arg) + for key, default in kwargs.items(): + kwarg_to_param(key, default) + + def params_to_kwargs(self): + ''' Inverse of kwargs_to_params. Converts the parameters of this instance and their chosen values + back into arguments that can be sent to the generating_function. + ''' + # todo: handle non-keyword args + all_args = list() + all_kwargs = dict() + for pdecl, pval in zip(self.get_parameters(), self.get_values()): + all_kwargs[pdecl.name] = pval + return all_args, all_kwargs + + def __init__(self, generating_function): + self.generating_function = generating_function + super().__init__() + self.kwargs_to_params() + + def display_text_impl(self): + ''' Produces a string that includes all of the parameters. This means it is unique for identical cells. + We assume that the pcells are functional in the sense that identical parameters yield identical geometry. + In this way, different pcell instances with the same arguments are correctly identified as the same cell. + ''' + text = self.generating_function.__name__ + '_' + for pdecl, pval in zip(self.get_parameters(), self.get_values()): + # sanitize _'s and ='s + if isinstance(pval, str): + pval.replace('_', '[_]') + pval.replace('=', '[=]') + text += '_{}={}'.format(pdecl.name, pval) + return text + + def produce_impl(self): + ''' Creates a fixed cell instance based on the previously specified parameters + ''' + # Produce the geometry + args, kwargs = self.params_to_kwargs() + phidl_Device = self.generating_function(*args, **kwargs) + # Convert phidl.Device to pya.Cell - just geometry + anyCell_to_anyCell(phidl_Device, self.cell) + # Transfer other data (ports, metadata, CML files, etc.) + pass # TODO + + + class WrappedLibrary(pya.Library): + ''' An abstract library consisting of pya PCells that are + based on function calls that possibly involve other languages (specified in all_funcs_to_wrap) + The names of the PCells will end up being the same as the names of the functions. + + Initializing the library registers it in klayout's repository of libraries. + + To subclass this class, no extra methods are needed. + Just override the class attributes. ''' - # Produce the geometry - args, kwargs = self.params_to_kwargs() - phidl_Device = self.generating_function(*args, **kwargs) - # Convert phidl.Device to pya.Cell - just geometry - anyCell_to_anyCell(phidl_Device, self.cell) - # Transfer other data (ports, metadata, CML files, etc.) - pass # TODO - - -class WrappedLibrary(pya.Library): - ''' An abstract library consisting of pya PCells that are - based on function calls that possibly involve other languages (specified in all_funcs_to_wrap) - The names of the PCells will end up being the same as the names of the functions. - - Initializing the library registers it in klayout's repository of libraries. - - To subclass this class, no extra methods are needed. - Just override the class attributes. - ''' - tech_name = None - all_funcs_to_wrap = None - description = None + tech_name = None + all_funcs_to_wrap = None + description = None - def __init__(self): - if self.tech_name is None or self.all_funcs_to_wrap is None: - raise NotImplementedError('WrappedLibrary must be subclassed.') + def __init__(self): + if self.tech_name is None or self.all_funcs_to_wrap is None: + raise NotImplementedError('WrappedLibrary must be subclassed.') - print("Initializing '%s' Library." % self.tech_name) + print("Initializing '%s' Library." % self.tech_name) - # Not doing fixed GDS in this Library + # Not doing fixed GDS in this Library - # Create all the new klayout-format PCells - for func in self.all_funcs_to_wrap: - self.layout().register_pcell(func.__name__, WrappedPCell(func)) # generic version + # Create all the new klayout-format PCells + for func in self.all_funcs_to_wrap: + self.layout().register_pcell(func.__name__, WrappedPCell(func)) # generic version - self.register(self.tech_name) + self.register(self.tech_name) - if int(pya.Application.instance().version().split('.')[1]) > 24: - # KLayout v0.25 introduced technology variable: - self.technology = self.tech_name + if int(pya.Application.instance().version().split('.')[1]) > 24: + # KLayout v0.25 introduced technology variable: + self.technology = self.tech_name