def text2stage(self, text, name=None, strict=ConversionLevel.NoFail, force_text=False): """ Create a new stage from a COMM text snippet. See `import_stage()` for details about *strict* mode. Arguments: text (str): COMM code snippet. name (str): Name of Stage being created. strict (ConversionLevel): Tells how strict the conversion must be. For more details, see `general.ConversionLevel`. Default is not to fail. Returns: Stage: New Stage. """ stage = self.create_stage(name) is_graphical_mode = not force_text and stage.is_graphical_mode() stage.use_text_mode() stage.set_text(text) if is_graphical_mode: try: stage.use_graphical_mode(strict) except Exception as exc: # pragma pylint: disable=broad-except debug_message('can not use graphical mode: {0}'.format(exc)) return stage
def debug_ajs(js_text): # pragma: no cover """For debugging, use one file name per session""" if not debug_mode(): return if not hasattr(debug_ajs, "cache"): debug_ajs.cache = tempfile.mkstemp(prefix='astdy-', suffix='.ajs')[1] with open(debug_ajs.cache, "w") as js_file: js_file.write(js_text) debug_message("File saved:", debug_ajs.cache)
def _debug_parse(num, val, line): # pragma: no cover """Debug helper""" if debug_mode(): if num == token.NEWLINE: snum = "*" * 12 elif num == tokenize.NL: snum = "+" * 12 elif num == tokenize.COMMENT: snum = "#" * 12 else: snum = token.tok_name[num] fmt = "{0:<12} {1!r:<20}: {2!r}" debug_message(fmt.format(snum, val, line))
def convert(self, intext): """Fill the stage from a commands file (given as text). Arguments: intext (str): Content of the commands file. """ init_size = len(self._stg) intext = to_unicode(intext) try: eoi, text = change_text(intext, self._strict) except Exception as exc: # pragma pylint: disable=broad-except details = traceback.format_exc() raise ConversionError(exc, details, '?', '?') text = "{0}\n_post_conversion() # {1}\n".format(text, MARK) self._exec_ctxt = self._setup() try: exec text in self._exec_ctxt # pragma pylint: disable=exec-used except Exception as exc: # pragma pylint: disable=broad-except self._teardown() details = traceback.format_exc() eoprev, lineno, line = _locate_error(exc, eoi, text) if self._strict & ConversionLevel.Partial: debug_message("Conversion failed, split after line", eoprev) # remove the unfinished command if self._unfinish: del self._stg[self._unfinish] # _post_conversion was not called try: self._post_conversion() except Exception: # pragma pylint: disable=broad-except # some commands have not be named... pass part1, astext = split_text(intext, eoprev, remove=MARK) # debug_message("+" * 50) # debug_message(part1) # debug_message("-" * 50) # debug_message(astext) # debug_message("=" * 50) # in case of SyntaxError, nothing has been added if isinstance(exc, SyntaxError): debug_message("SyntaxError: convert first part separately") comm2study(part1, self._stg, self._strict) if astext: self.add_text_stage(astext) else: # revert the input stage as it was while len(self._stg) > init_size: del self._stg[-1] raise ConversionError(exc, details, lineno, line) finally: self._teardown()
def update(self, expression=None, name=None): """Evaluates assigned expressions in the `current` context. Ensure to be not recursive. Returns: dict: pairs of name per Python instance. """ if self._updating: return self._updating = True debug_message("updating variable", repr(self)) try: return self._update(expression, name) finally: self._updating = False
def _change_text(text): """Pre-processing of the input text. - Wrap constant parameters: ``a = 1`` is converted as ``a = _CONVERT_VARIABLE(EXPR="1")`` - Wrap comments: ``# line of comment.`` is converted as ``_CONVERT_COMMENT(EXPR="# line of comment.")`` Returns: list[int]: list of line numbers of end of instruction. str: changed text. """ generator = tokenize.generate_tokens(StringIO(text).readline) result = [] buff = [] eoi = [] started = False for ret in generator: num, val = ret[:2] started = started or num == token.NAME # _debug_parse(num, val, ret[4]) if num == token.NEWLINE: eoi.append(ret[2][0]) buff.append((num, val)) if num in (token.NEWLINE, token.ENDMARKER): buff = _replace_variable(buff) started = False elif num == tokenize.COMMENT and len(buff) == 1: # ignore inline comment buff = _replace_comment(buff) started = False if not started: result.extend(buff) # _debug_parse(tokenize.COMMENT, "> > > new buffer > > >", "???") buff = [] changed = tokenize.untokenize(result) debug_message("Pre-processed text:\n", changed) return eoi, changed
def comm2study(content, stage, strict=ConversionLevel.NoFail): """Import a text of code_aster commands into the *Stage* object. Arguments: content (str): Text of code_aster commands to import. stage (Stage): *Stage* in which the *Command* objects are added. strict (ConversionLevel): Tells how strict the conversion must be. See `general.ConversionLevel` for more details. Default is not to fail. """ nbs = stage.parent_case.nb_stages debug_message("starting new conversion of stage", stage.number, "/", nbs) assert not strict & ConversionLevel.Partial or stage.number == nbs builder = CommandBuilder(stage, strict) try: builder.convert(content) stage.reorder() finally: builder.reset_callbacks()
def _locate_error(exc, eoi, text): """Return the line number and the line where the exception occurred. Returns: int: Line number of the end of the previous (successfull) instruction. int: Line number of the error. line: Line content where the error occurred. """ if isinstance(exc, SyntaxError): lineno = exc.args[1][1] line = exc.args[1][3].strip() else: # 0: here, 1: executed text ltb = traceback.extract_tb(sys.exc_info()[-1], limit=3) try: tbck = ltb[1] lineno = tbck[1] except IndexError: # pragma: no cover lineno = -1 debug_message("Error at line", lineno, "end of instr", eoi) lines = text.splitlines() upto = [] offset = 0 for i in eoi: if i >= lineno: break orig = i - offset if lines[i - 1].startswith("raise NotImplementedError"): offset += 1 else: upto.append(orig) eoprev = upto.pop(-1) if upto else 0 debug_message("Previous instr at line", eoprev, "(original text)") if not isinstance(exc, SyntaxError): line = lines[lineno - 1] if len(lines) > lineno - 2 else "?" return eoprev, lineno, line
def _insert_id(self, command): """Insert a command at the right position.""" self._discard_position(command) idx = 0 ids = self._ids cmdid = command.uid for i, uid in enumerate(ids): # if command is a child of cmd_i if self._model.has_path(uid, cmdid): pass # elif cmd_i is a child of command: elif self._model.has_path(cmdid, uid): break # no direct dependencies else: # if command depends on a command that follows, # do not use other criteria deps = False for idj in ids[i + 1:]: # if command is a child of cmd_idj: if self._model.has_path(idj, cmdid): deps = True break if deps: idx += 1 continue cmd_i = self.get_cmd(uid) if command.categ < cmd_i.categ: # debug_message("criteria: category", level=2) break elif command.categ == cmd_i.categ: if command.uid < uid: # debug_message("criteria: creation order", level=2) break idx += 1 debug_message("insert at", idx, ':', command.title, repr(command)) ids.insert(idx, command.uid)
def _fill_categories(self): """Fill categories map.""" not_found_msg = "Command {0} is not found in catalogue" # 0. First of all add special category for variables self._command_to_category['_CONVERT_VARIABLE'] = 'Variables' self._categories['Variables'] = [] # 1. Fill in categories in proper order for category in CATEGORIES_DEFINITION: self._categories[category] = [] for name in CATEGORIES_DEFINITION[category]: command = self._catalogs.get(name) if not command: debug_message(not_found_msg.format(name)) else: self._categories[category].append(name) self._command_to_category[name] = category # Finally, add a category for deprecated commands self._categories['Other'] = [] self._categories['Deprecated'] = [] self._categories['Hidden'] = [] for name in DEPRECATED: self._categories['Deprecated'].append(name) self._command_to_category[name] = 'Deprecated' # 2. Put remaining commands to the 'Other' category for name in self._catalogs: if self._command_to_category.get(name): continue # fake commands starts with "_": hidden if name.startswith("_"): category = 'Hidden' else: category = 'Other' self._categories[category].append(name) self._command_to_category[name] = category self._categories['Other'].sort()
def document2history(bdocument, strict=STRICT_DEFAULT, aster_version=None): # pragma pylint: disable=too-many-locals """Converts AsterStudy ProtoBuffer message to History instance Arguments: bdocument (BDocument): AsterStudy ProtoBuffer document. strict (Optional[ConversionLevel]): Tells how strict the conversion must be. aster_version (Optional[str]): code_aster version used instead of those stored in the document. """ from .history import History from common import ConversionError from .dataset import DataSet from .result import Job bhistory = bdocument.history history = History(bhistory.aster if aster_version is None \ else aster_version) bvers = bhistory.versionMajor, bhistory.versionMinor, bhistory.versionPatch if bvers != history.version_number: sbvers = ".".join([str(i) for i in bvers]) snumb = ".".join([str(i) for i in history.version_number]) msgerr = ("The study was created using the '{0}' version as {1} " "but the available '{0}' version is {2}").format( history.version, sbvers, snumb) if strict & ConversionLevel.Restore: raise ValueError(msgerr) else: debug_message(msgerr) history.jobs_list = bhistory.jobs_list uid2stage = {} for idx, bcase in enumerate(bhistory.cases): name = bcase.name if idx == 0: case = history.current_case case.name = name else: case = history.create_case(name)
def save(self, directory, url): """ Save module data to files; returns file names. The function saves the module data to the files in a temporary directory specified as a parameter and returns names if files in which module data is saved. Arguments: directory (str): A directory to store data files. Note: this can be not a final study destination folder but a temporary directly, depending on used save mode (single-file or multi-file). url (str): Actual study URL (the final study destination). Note: this parameter is provided for information purposes only! Depending on version of SALOME being used this parameter may be empty! Returns: list[str]: names of files in which data is saved """ try: study_name = get_salome_pyqt().getStudyName() + "_" except AttributeError: study_name = get_base_name(url, False) + "_" if url else "" ajs = "{}asterstudy.{}".format(study_name, study_extension()) path = os.path.join(directory, ajs) debug_message("salomegui.save(): ajs: {0}, url: {1}".format(ajs, url)) self.study().set_url(url) try: self.study().saveAs(path) except IOError: ajs = "" self._updateActions() files = [to_str(ajs)] files.extend(self.study().history.save_embedded_files(directory)) return files
def load(self, files, url): """ Load data from the files; return result status. The function restores module data from the files specified as a parameter; returns *True* in case of success or *False* otherwise. Arguments: files (list[str]): Data files in which module data is stored. Note: first element of this list is a directory name. File names are normally specified as relative to this directory. url (str): Actual study URL (the original study file path). Note: this parameter is provided for information purposes only! Depending on version of SALOME being used this parameter may be empty! Returns: bool: *True* in case of success; *False* otherwise """ debug_message("salomegui.load(): url: {0}, files: {1}".format( url, files)) # do nothing if url is empty if not url: return False try: ajs = os.path.join(files[0], files[1]) self._setStudy(Study.load(self, ajs, url)) self.study().set_url(url) self.study().loadEmbeddedFilesWrapper(files[0], files[2:]) return True except IOError: pass return False
def _check_deleters(command, deleters): """Check if a Command reuse a name of a previously deleted result. In this case, the Command must depend on the deleter. Arguments: command (Command): Command currently checked. deleters (dict): Dict `{deleted: deleter}`. """ delnames = [i.name for i in deleters.keys()] name = command.name if name in ('', '_'): return if name in delnames: # add a dependency to DETRUIRE of the previous 'name' delcmds = [(i, val) for i, val in deleters.iteritems() \ if i.name == name and i != command] if not delcmds: return key, deleter = delcmds[0] del deleters[key] debug_message("add deps {0.title}<{0.uid}> vers {1.title}<{1.uid}>" .format(command, deleter)) add_parent(command, deleter)
def add_text_stage(self, text): """Add a text stage for the text that can not be converted. Arguments: text (str): The text that can not be converted. """ debug_message("add a text stage containing:\n", text) stg = self._stg case = self._stg.parent_case if stg.number != case.nb_stages: # a text stage was already inserted: concatenate stg = case[case.nb_stages - 1] text = stg.get_text(pretty=False) + os.linesep + text elif len(stg) != 0: # some commands have been added, create a new text stage newname = "{0}_{1}".format(stg.name, case.nb_stages) stg = case.create_stage(newname) try: text = format_code(text) except SyntaxError: pass stg.use_text_mode() stg.set_text(text)
def read_catalogs(self, version=None): """ Read all catalogs. Arguments: version (str): Version of code_aster catalogs. """ if version and version == self._version: debug_message("Catalog for {0!r}: already loaded".format(version)) return self.reset() if not version: version = CFG.default_version debug_message("Loading catalog for {0!r}".format(version)) version_path = self.version_path(version) debug_message("from path {0!r}".format(version_path)) # Enable marker AsterStudySession.set_cata() try: self._pkgs = import_aster(version_path) commands = self._pkgs["Commands"].__dict__ except ImportError as exc: debug_message("Can not import version {0!r}\nReason: {1}" .format(version_path, exc)) commands = {} if commands: self._add_conversion_commands() for key, value in commands.iteritems(): if get_cata_typeid(value) == IDS.command: self._catalogs[key] = value self._fill_categories() self._version = version self._read_dockeys()
def _post_conversion(self): """Post-conversion function to name the results in *Command* objects. Arguments: _locals (dict): Context of the comm file. """ _locals = self._exec_ctxt _unauthorized = [i for i in CATA] # store the name of code_aster datastructures result_names = {} for name, result in _locals.viewitems(): if isinstance(result, CATA.baseds): if name in _unauthorized: raise ValueError("Unauthorized name: {0!r}".format(name)) result_names[result] = name debug_message("naming result", result, "as", name) # store results names in Command objects and evaluate user variables results = result_names.keys() # it is important to loop on commands by creation order for DETRUIRE deleters = OrderedDict() for cmd in self._stg: if cmd in self._del: deleters[cmd] = self._del[cmd] _check_deleters(cmd, deleters) result = self._cmd_results.get(cmd) if result is None: continue if result in results: cmd.name = result_names[result] debug_message("naming result of", cmd.title, ":", cmd.name) elif cmd in self._reuse: # because of tolerated syntax "cmd(reuse='xx', ...)" cmd.name = self._reuse[cmd].name debug_message("reuse name for", cmd.title, ":", cmd.name) if self._strict & ConversionLevel.Naming and \ cmd.name in ('', '_'): raise NotImplementedError("can not name command: {0}" .format(cmd))
def displayMEDFileName(self, meshfile, meshname=None, opacity=1.0, erase=False): """Redefined from *MeshBaseView*.""" debug_message("entering displayMEDFileName...") import salome entry = self.getMeshEntry(meshfile, meshname) # activate Asterstudy's VTK view with help of the SalomePyQt utility of # SALOME's GUI module from ..salomegui import get_salome_pyqt get_salome_pyqt().activateViewManagerAndView(self._vtk_viewer) if meshfile == self._displayed_mesh[0] \ and meshname == self._displayed_mesh[1] \ and not erase: self.setAspect(entry, opacity) salome.sg.UpdateView() debug_message("displayMEDFileName return #1") return # display the entry in the active view with help of the `sg` python # module of `salome` python package for dentry in self._diplayed_entry: self._diplayed_entry[dentry] = 0 salome.sg.EraseAll() salome.sg.Display(str(entry)) self._diplayed_entry[entry] = 1 self._displayed_mesh = (meshfile, meshname) self.setAspect(entry, opacity) salome.sg.FitAll() salome.sg.UpdateView() debug_message("displayMEDFileName return final")
if strict & ConversionLevel.Restore: raise ValueError(msgerr) else: debug_message(msgerr) history.jobs_list = bhistory.jobs_list uid2stage = {} for idx, bcase in enumerate(bhistory.cases): name = bcase.name if idx == 0: case = history.current_case case.name = name else: case = history.create_case(name) debug_message("loading case {0!r}...".format(name)) case.base_folder = bcase.base_folder case.description = bcase.description case.is_backup = bcase.is_backup if bcase.in_dir: case.in_dir = bcase.in_dir if bcase.out_dir: case.out_dir = bcase.out_dir debug_message("loading stages...") for stageid in bcase.stages: if stageid in uid2stage: case.add_stage(uid2stage[stageid]) else: bstage = bhistory.stages[stageid - 1]
def _exec_command(self, ascommand, **kwargs): """Execution of the *ascommand*.""" debug_message("executing aster command", ascommand.name) parents = [] if len(self._stg) > 0: last = self._stg.dataset.get_last_command() if last.title == "_CONVERT_COMMENT": if ascommand.name == "_CONVERT_COMMENT": # append the text of the comment to the previous comment debug_message("append to last comment", kwargs) last["EXPR"] = last["EXPR"].value + "\n" + kwargs["EXPR"] return # add a dependency to the previous comment parents.append(last) kwargs = add_reuse_badcommands(ascommand, kwargs) kwargs = add_unit_default_value(ascommand, kwargs) result = ascommand.call_default(__strict__=self._strict, **kwargs) debug_message("creating Command for", ascommand.name) command = self.add_command(ascommand.name, **kwargs) # store the result produced by the Command. if result is not None: self._cmd_results[command] = result self._results_cmd[result] = command debug_message("result stored for", command.title, result) # DETRUIRE if command.title == "DETRUIRE": concept = command["CONCEPT"] if isinstance(concept, Factor): concept = [concept, ] for fact in concept: names = fact["NOM"].value if not isinstance(names, (list, tuple)): names = [names, ] for cmd in names: self._del[cmd] = command # if a macro-command produces additional results for hidden in command.hidden: hidden.check(safe=False) self._exec_ctxt[hidden.name] = hidden # In case of "reuse", a command result may be reset by the current # command, that's why we must name it now and not wait # for `_post_conversion()`. # Name previously created objects _locals = self._exec_ctxt for name, item in _locals.viewitems(): if not isinstance(item, CATA.baseds): continue cmd = self._results_cmd[item] cmd.name = name debug_message("name updated", name, "for", cmd.title, item) for i in parents: debug_message("adding parent of", command.title, ":", i.uid) add_parent(command, i) return result
def _set(self, store, value): """Set the value in dict""" if not store.has_key(self._current): debug_message("Add default value: {0}={1!r}".format( self._current, value)) store[self._current] = value