def read(self, *a): """Read data""" logmgr_flog() data = self.f.read(*a) self.offset.extend(accumulate(len(m)+1 for m in data.split('\n'))) return data
def ensurefileobj(source): """Return a file(-like) object, regardless if it's a another file-object, a filename, or a string :param source: filename, file-like object, or string :return: StringIO or file-like object """ logmgr_flog() # StringIO support: if hasattr(source, 'getvalue') and hasattr(source, 'tell'): # we return the source return source elif isinstance(source, (str, bytes)): if is_xml(source): return StringIO(source) else: # source isn't a file-like object nor starts with XML structure # so it has to be a filename try: res = StringIO(open(source, 'r').read()) except FileNotFoundError as err: # pylint:disable=undefined-variable raise DMFileNotFoundError("Could not find file {!r}.".format(err.filename), err.filename, ReturnCodes.E_FILE_NOT_FOUND) # pylint: enable=undefined-variable return res
def replace_entities(self): """This function replaces entities in the StringIO buffer """ logmgr_flog() self._buffer.seek(self._offset) self._buffer = replaceinstream(self._buffer, preserve_entities)
def create_group(self): """Creates the docmanager group element""" logmgr_flog() #search the info-element if not exists raise an error info = self.__tree.find("//d:info", namespaces=NS) # TODO: We need to check for a --force option if info is None: log.debug("No <info> element found!") pos = findinfo_pos(self.__root) log.debug("Using position %d", pos) info = etree.Element("{%s}info" % NS["d"]) info.tail = '\n' info.text = '\n' self.__root.insert(pos, info) log.debug("Adding <info> element in '%s'", self.filename) log.debug("Adding <dm:docmanager> to <info>") # dm = etree.Element("{%s}docmanager" % NS["dm"]) # self.__docmanager = info.insert(0, dm) self.__docmanager = etree.SubElement(info, "{{{dm}}}docmanager".format(**NS), nsmap={'dm': NS['dm']}, )
def set(self, pairs): """Sets the key as element and value as content :param key: name of the element :param value: value that this element will contain If key="foo" and value="bar" you will get: <foo>bar</foo> whereas foo belongs to the DocManager namespace """ logmgr_flog() #import pdb #pdb.set_trace() dm = self.__docmanager dmelem = list() lastnode = dm for key in pairs: elemlist = key.split("/") for e in elemlist: name = "dm:" + e dmelem.append(name) node = dm.find("/".join(dmelem), namespaces=NS) if node is None: node = etree.SubElement(lastnode, "{{{dm}}}{key}".format(key=e, **NS)) lastnode = node node.text = "" node.text = pairs[key]
def delete(self, key, condition=None): """Deletes an element inside docmanager element :param str key: element name to delete :param str condition: the condition for the deletion (the var condition has to be equal with the property value) :return boolean: True = success | False = no property has been deleted """ logmgr_flog() key = key.split("/") lastnode = None key_handler = self.__docmanager.find("dm:{}".format(key[0]), namespaces=NS) for idx, prop in enumerate(key): if lastnode is not None: key_handler = lastnode.find("dm:{}".format(prop), namespaces=NS) lastnode = key_handler if key_handler is None: break if idx == len(key)-1: if condition is not None: if condition != key_handler.text: break key_handler.getparent().remove(key_handler) return True return False
def compilestarttag(roottag=None): """Compile a regular expression for start tags like <article> or <d:book> with or without any attributes :param str roottag: Name of roottag or None, for a general tag :return: a pattern object :rtype: _sre.SRE_Pattern """ logmgr_flog() # Taken from the xmllib.py # http://code.metager.de/source/xref/python/jython/lib-python/2.7/xmllib.py _S = '[ \t\r\n]+' # white space _opS = '[ \t\r\n]*' # optional white space _Name = '[a-zA-Z_:][-a-zA-Z0-9._:]*' # valid XML name _QStr = "(?:'[^']*'|\"[^\"]*\")" # quoted XML string attrfind = re.compile( _S + '(?P<name>' + _Name + ')' '(' + _opS + '=' + _opS + '(?P<value>' + _QStr + '|[-a-zA-Z0-9.:+*%?!\(\)_#=~]+))?') starttagend = re.compile(_opS + '(?P<slash>/?)>') if roottag: root = '<(?P<tagname>' + roottag + ')' else: root = '<(?P<tagname>' + _Name + ')' return re.compile(root + '(?P<attrs>(?:' + attrfind.pattern + ')*)' + starttagend.pattern)
def indent_dm(self): """Indents only dm:docmanager element and its children""" logmgr_flog() dmindent=' ' dm = self.__tree.find("//dm:docmanager", namespaces=NS) log.debug("dm is %s", dm) if dm is None: return log.debug("-----") info = dm.getparent() #.getprevious() log.info("info: %s", info) prev = info.getprevious() log.info("prev: %s", prev) parent = info.getparent() log.info("parent of info: %s", parent) log.info("child of info: %s", info.getchildren()) if info.tail is None: info.tail = "" infoindent = "".join(info.tail.split('\n')) prev = dm.getprevious() #log.info("prev: %s", prev) if prev is not None: log.info("prev: %s", prev) prev.tail = '\n' + infoindent indent=self.get_indentation(dm.getprevious()) dm.text = '\n' + indent + ' ' dm.tail = '\n' + infoindent for node in dm.iterchildren(): i = dmindent if node.getnext() is not None else '' node.tail = '\n' + indent + i
def set(self, arguments): """Set key/value pairs from arguments :param list arguments: List of arguments with key=value pairs """ logmgr_flog() # count all valid and invalid xml files validfiles, invalidfiles = self.get_files_status(self.__xml) # split key and value args = [i.split("=") for i in arguments] # iter through all key and values for f in self.__files: if "error" in self.__xml[f]: print("[ {} ] {} -> {}".format(red("error"), f, red(self.__xml[f]['errorstr']))) else: for arg in args: try: key, value = arg if key == "languages": value = value.split(",") value = ",".join( self.remove_duplicate_langcodes(value)) log.debug( "[%s] Trying to set value for property " "%r to %r.", f, key, value) if self.__args.bugtracker: self.__xml[f]["handler"].set( {"bugtracker/" + key: value}) else: self.__xml[f]["handler"].set({key: value}) except ValueError: log.error('Invalid usage. ' 'Set values with the following format: ' 'property=value') sys.exit(ReturnCodes.E_INVALID_USAGE_KEYVAL) print("[ {} ] Set data for file {}.".format(green("ok"), f)) # save the changes for f in self.__files: if "error" not in self.__xml[f]: log.debug("[%s] Trying to save the changes.", f) self.__xml[f]["handler"].write() # print the statistics output print( "\nWrote {} valid XML file{} and skipped {} XML file{} due to errors." .format(green(validfiles), '' if validfiles == 1 else 's', red(invalidfiles), '' if invalidfiles == 1 else 's')) if invalidfiles > 0: sys.exit(ReturnCodes.E_SOME_FILES_WERE_INVALID)
def startDTD(self, doctype, publicID, systemID): # pylint:disable=unused-argument """Signals the start of an DTD declaration :param doctype: name of the root element :param publicID: public identifier (or empty) :param systemID: system identifier (or empty) """ logmgr_flog()
def setDocumentLocator(self, locator): """Called by the parser to give the application a locator for locating the origin of document events. :param LocatingWrapper loc: LocatingWrapper object """ logmgr_flog() self.loc = locator
def delete(self, arguments): """Delete a property :param list arguments: """ logmgr_flog() # statistics variables file_errors = 0 props_failed = 0 props_deleted = 0 # delete the properties for f in self.__files: if "error" in self.__xml[f]: print("[{}] {} -> {}".format(red(" error "), f, red(self.__xml[f]["errorstr"]))) file_errors += 1 else: failed_properties = list() for arg in arguments: cond = None prop = arg pos = arg.find("=") # look if there is condition if pos != -1: prop = arg[:pos] cond = arg[pos + 1:] if not self.__xml[f]["handler"].delete(prop, cond): failed_properties.append(arg) props_failed += 1 else: props_deleted += 1 if not failed_properties: print("[{}] {}".format(green(" ok "), f)) else: print("[{}] {} -> Couldn't delete these properties: {}". format(yellow(" info "), f, ", ".join(failed_properties))) # save changes for f in self.__files: if "error" not in self.__xml[f]: self.__xml[f]["handler"].write() # print statistics print("") print( "Deleted successfully {} propert{}, {} propert{} couldn't be deleted, and {} {} invalid." .format(green(props_deleted), 'ies' if props_deleted != 1 else 'y', yellow(props_failed), 'ies' if props_failed != 1 else 'y', red(file_errors), 'files were' if file_errors != 1 else 'file was'))
def recover_entities(text): """Recover any preserved entities in text :param str text: the text that should recover entities :return: the recovered text :rtype: str """ logmgr_flog() return STEN.sub(txt2ent, text)
def where(self, locator): """Returns the offset from line and column :param locator: locator object :return: offset :rtype: int """ logmgr_flog() return self.offset[locator.getLineNumber() - 1] + locator.getColumnNumber()
def parse(self): logmgr_flog() action = self.__args.action if hasattr(self, action) and getattr(self, action) is not None: log.debug("Action.__init__: %s", self.__args) return getattr(self, action)(self.__args.properties) else: log.error("Method \"%s\" is not implemented.", action) sys.exit(ReturnCodes.E_METHOD_NOT_IMPLEMENTED)
def preserve_entities(text): """Preserve any entities in text :param str text: the text that should preserve entities :return: the preserved text :rtype: str """ logmgr_flog() return ENTS.sub(ent2txt, text)
def txt2ent(match): """Replace any [[[text]]] -> &text; :param _sre.SRE_Match match: match object from re :return: replaced string :rtype: str """ logmgr_flog() if match: return "&{};".format(match.group(2))
def check_root_element(self): """Checks if root element is valid""" logmgr_flog() tag = etree.QName(self.__root.tag) if tag.localname not in VALIDROOTS: raise DMInvalidXMLRootElement("Cannot add info element to file %r. " "This file does not contain a valid " "DocBook 5 root element. Found %s", self._filename, localname(self.__root.tag), ReturnCodes.E_INVALID_ROOT_ELEMENT)
def check_root_element(rootelem, etree): """Checks if root element is valid :param object: root element (object) :param object: etree element (etree object)""" logmgr_flog() tag = etree.QName(rootelem.tag) if tag.localname not in VALIDROOTS: raise DMInvalidXMLRootElement("Cannot add info element to %s. " "Not a valid root element." % tag.localname, ReturnCodes.E_INVALID_ROOT_ELEMENT)
def get_all(self): """Returns all keys and values in a docmanager xml file """ logmgr_flog() ret = OrderedDict() for idx, i in enumerate(self.__docmanager.iter()): # we want to skip the "docmanager" element here if idx: xpath = get_property_xpath(i) ret[xpath] = i.text return ret
def localname(tag): """Returns the local name of an element :param str tag: Usually in the form of {http://docbook.org/ns/docbook}article :return: local name :rtype: str """ logmgr_flog() m = NAMESPACE_REGEX.search(tag) if m: return m.groupdict()['local'] else: return tag
def get_namespace(tag): """Returns the namespace of an element :param str tag: Usually in the form of {http://docbook.org/ns/docbook}article :return: namespace of the element :rtype: str """ logmgr_flog() m = NAMESPACE_REGEX.search(tag) if m: return m.groupdict()['ns'] else: return ''
def get_indentation(self, node, indentation=""): """Calculates indentation level :param lxml.etree._Element node: node where to start :param str indentation: Additional indentation """ logmgr_flog() indent = "" if node is not None: indent = "".join(["".join(n.tail.split("\n")) for n in node.iterancestors() if n.tail is not None ]) return indent+indentation
def ent2txt(match, start="[[[", end="]]]"): """Replace any &text; -> [[[text]]] :param _sre.SRE_Match match: match object from re :param str start: Start string of entity replacement :param str end: end string :return: replaced string :rtype: str """ logmgr_flog() if match: return "{}{}{}".format(start, match.group(2), end)
def endElement(self, name): """Signals the end of an element in non-namespace mode :param str name: XML 1.0 Name of the element """ logmgr_flog() eline = self.loc.getLineNumber() ecol = self.loc.getColumnNumber() last = self.locstm.where(self.loc) pos = self.pos(line=eline, col=ecol, offset=last) # save the position of an end tag and add '/' in front of the # name to distinguish it from a start tag self.context.append(["/%s" % name, pos])
def findinfo_pos(root): """Find the position where to insert the <info> element :return: position where to insert <info> :rtype: int """ logmgr_flog() titles = root.xpath("(d:title|d:subtitle|d:titleabbrev)[last()]", namespaces=NS) if not titles: # Just in case we didn't find any titles at all, return null return 0 return root.index(titles[0]) + 1
def comment(self, text): # pylint: disable=unused-argument """Signals an XML comment :param str text: text content of the XML comment """ logmgr_flog() ctxlen = len(self.context) # We are only interested in the first two start tags if ctxlen: current = self.locstm.where(self.loc) pos = self.pos(self.loc.getLineNumber(), \ self.loc.getColumnNumber(), \ current) self.context.append(["-- comment", pos])
def startElement(self, name, attrs): """Signals the start of an element in non-namespace mode :param str name: XML 1.0 Name of the element :param Attributes attrs: attributes of the current element """ logmgr_flog() ctxlen = len(self.context) # We are only interested in the first two start tags if ctxlen < 2: current = self.locstm.where(self.loc) pos = self.pos(self.loc.getLineNumber(), \ self.loc.getColumnNumber(), \ current) self.context.append(["%s" % name, pos])
def is_prop_set(self, prop): """ Checks if a property is set in an XML element :param str prop: the property :return: if property is set :rtype: bool """ logmgr_flog() element = self.__docmanager.find("./dm:{}".format(prop), namespaces=NS) if element is not None: return True return False
def processingInstruction(self, target, data): """Receive notification of a processing instruction (PI) :param str target: the target of the PI :param str data: the data of the PI """ logmgr_flog() ctxlen = len(self.context) # Only append PIs when it's NOT before start-tag if ctxlen: current = self.locstm.where(self.loc) pos = self.pos(self.loc.getLineNumber(), \ self.loc.getColumnNumber(), \ current) self.context.append(["?%s" % target, pos])