def export (self, path, rev, dstpath, rewrite=None): # Base override. if rev is None: res = collect_system(["svn", "export", "--force", self._ep(path), "-r", "BASE", dstpath]) if res[-1] != 0: return False return True res = collect_system(["svn", "info", self._ep(path)], env=self._env) if res[-1] != 0: return False rx = re.compile(r"^URL:\s*(\S+)", re.I) rempath = None for line in res[0].split("\n"): m = rx.search(line) if m: rempath = m.group(1) break if not rempath: return False if rewrite: rempath = rewrite(rempath, rev) if collect_system(["svn", "export", "--force", self._ep(rempath), "-r", rev, dstpath])[-1] != 0: return False return True
def translate(self, texts): # Serialize texts to send to Apertium in one go. # Separate texts with an inplace tag followed by dot, # to have each text interpreted as standalone sentence. # FIXME: Any way to really translate each text in turn, # without it being horribly slow? sep0 = "<br class='..." sep1 = "..." sep2 = "'/>." sep = None nsep = 0 while not sep: # determine shortest acceptable separator sep = sep0 + sep1 * nsep + sep2 for text in texts: if sep in text: sep = None nsep += 1 break stext = sep.join(texts) # Translate empty string to test language pair. # Otherwise, if a lot of text is sent and language pair not good, # Apertium may just signal broken pipe. res = collect_system(self.cmdline, instr="") if res[2] != 0: warning( _("@info", "Executing Apertium failed:\n%(output)s", output=res[0])) # ...really res[0], error is output to stdout. Tsk. return None res = collect_system(self.cmdline, instr=stext) if res[2] != 0: warning( _("@info", "Executing Apertium failed:\n%(output)s", output=res[0])) # ...really res[0], error is output to stdout. Tsk. return None texts_tr = res[0].split(sep) if len(texts_tr) != len(texts): warning( _("@info", "Apertium reported wrong number of translations, " "%(num1)d instead of %(num2)d.", num1=len(texts_tr), num2=len(texts))) return None texts_tr = [ resolve_entities_simple(x, self.htmlents) for x in texts_tr ] return texts_tr
def log (self, path, rev1=None, rev2=None): # Base override. res = collect_system(["svn", "log", self._ep(path)], env=self._env) if res[-1] != 0: return [] rev = "" next_rev, next_cmsg = range(2) entries = [] next = -1 for line in res[0].strip().split("\n"): if line.startswith("----------"): if rev: cmsg = "\n".join(cmsg).strip("\n") entries.append((rev, user, dstr, cmsg)) cmsg = [] next = next_rev elif next == next_rev: lst = line.split("|") rev, user, dstr = [x.strip() for x in lst[:3]] rev = rev[1:] # strip initial "r" next = next_cmsg elif next == next_cmsg: cmsg += [line] entries.reverse() return _crop_log(entries, rev1, rev2)
def export (self, path, rev, dstpath, rewrite=None): # Base override. root, path = self._gitroot(path) ret = True if rev is None: rev = "HEAD" if rewrite: path = rewrite(path, rev) # FIXME: Better temporary location." # FIXME: Use list command lines (so must replace piping too). tarpdir = "/tmp" tarbdir = "git-archive-tree%d" % os.getpid() res = collect_system(" git archive --prefix=%s/ %s %s " "| (cd %s && tar xf -)" % (tarbdir, rev, path, tarpdir), wdir=root) if res[2] == 0: tardir = os.path.join(tarpdir, tarbdir) tarpath = os.path.join(tardir, path) try: shutil.move(tarpath, dstpath) except: ret = False if os.path.isdir(tardir): shutil.rmtree(tardir) else: ret = False return ret
def move (self, spath, dpath): # Base override. if collect_system(["svn", "move", "--parents", self._ep(spath), dpath])[2] != 0: return False return True
def is_clear (self, path): # Base override. res = collect_system(["svn", "status", path], env=self._env) clear = not re.search(r"^\S", res[0]) return clear
def remove (self, path): # Base override. if collect_system(["svn", "remove", path])[2] != 0: return False return True
def _paths_to_commit (self, root, path=None): if path: cmdline = ["git", "status", path] else: cmdline = ["git", "status"] res = collect_system(cmdline, wdir=root, env=self._env) sect_rx = re.compile(r"^(?:# )?(\S.*):$", re.I) file_rx = re.compile(r"^#?\s+.*\w:\s*(.+?)\s*$", re.I) inlist = False ipaths = [] for line in res[0].split("\n"): m = sect_rx.search(line) if m: mstr = m.group(1) if ( mstr.endswith("to be committed") # git 1.6.x or mstr.endswith("but not updated") # git 1.7.x or mstr.endswith("not staged for commit") # git 1.7.x ): inlist = True else: break if not inlist: continue m = file_rx.search(line) if m: ipaths.append(m.group(1)) return ipaths
def log (self, path, rev1=None, rev2=None): # Base override. root, path = self._gitroot(path) res = collect_system(["git", "log", path], wdir=root, env=self._env) if res[-1] != 0: return [] rev = "" next_auth, next_date, next_cmsg = range(3) next = -1 entries = [] lines = res[0].split("\n") for i in range(len(lines) + 1): if i < len(lines): line = lines[i] if i == len(lines) or line.startswith("commit"): if rev: cmsg = "\n".join(cmsg).strip("\n") entries.append((rev, user, dstr, cmsg)) rev = line[line.find(" ") + 1:].strip() cmsg = [] next = next_auth elif next == next_auth: user = line[line.find(":") + 1:].strip() next = next_date elif next == next_date: dstr = line[line.find(":") + 1:].strip() next = next_cmsg elif next == next_cmsg: cmsg += [line[4:]] entries.reverse() return _crop_log(entries, rev1, rev2)
def revert (self, path): # Base override. res = collect_system(["svn", "revert", "-R", path], env=self._env) if res[-1] != 0: warning(_("@info", "Subversion reports it cannot revert path '%(path)s':\n" "%(msg)s", path=path, msg=res[1])) return False return True
def revert (self, path): # Base override. res = collect_system(["git", "checkout", path], wdir=root, env=self._env) if res[-1] != 0: warning(_("@info" "Git reports it cannot revert path '%(path)s':\n" "%(msg)s", path=path, msg=res[1])) return [] return True
def is_versioned (self, path): # Base override. res = collect_system(["svn", "info", self._ep(path)], env=self._env) if res[-1] != 0: return False rx = re.compile(r"^Repository", re.I) for line in res[0].split("\n"): if rx.search(line): return True return False
def revision (self, path): # Base override. res = collect_system(["svn", "info", self._ep(path)], env=self._env) rx = re.compile(r"^Last Changed Rev: *([0-9]+)", re.I) revid = "" for line in res[0].split("\n"): m = rx.search(line) if m: revid = m.group(1) break return revid
def to_commit (self, path): # Base override. res = collect_system(["svn", "status", path], env=self._env) if res[-1] != 0: return [] ncpaths = [] for line in res[0].split("\n"): if line[:1] in ("A", "M"): path = line[1:].strip() ncpaths.append(path) return ncpaths
def move (self, spath, dpath): # Base override. root1, spath = self._gitroot(spath) root2, dpath = self._gitroot(dpath) if root1 != root2: warning(_("@info", "Trying to move paths between different repositories.")) return False if collect_system(["git", "mv", spath, dpath], wdir=root1)[2] != 0: return False return True
def revision (self, path): # Base override. root, path = self._gitroot(path) res = collect_system(["git", "log", path], wdir=root, env=self._env) rx = re.compile(r"^commit\s*([0-9abcdef]+)", re.I) revid = "" for line in res[0].split("\n"): m = rx.search(line) if m: revid = m.group(1) break return revid
def remove (self, path): # Base override. if os.path.isdir(path): warning(_("@info", "Git cannot remove directories (tried on '%(path)s').", path=path)) return False root, path = self._gitroot(path) if collect_system(["git", "rm", path], wdir=root)[2] != 0: return False return True
def diff (self, path, rev1=None, rev2=None): # Base override. root, path = self._gitroot(path) if rev1 is not None and rev2 is not None: rspec = "%s..%s" % (rev1, rev2) elif rev1 is not None: rspec = "%s" % rev1 elif rev2 is not None: raise PologyError( _("@info" "Git cannot diff from non-staged paths to a commit.")) else: rspec = "" res = collect_system(["git", "diff", rspec, path], wdir=root, env=self._env) if res[-1] != 0: warning(_("@info" "Git reports it cannot diff path '%(path)s':\n" "%(msg)s", path=path, msg=res[1])) return [] udiff = [] nskip = 0 for line in res[0].split("\n"): if nskip > 0: nskip -= 1 continue if line.startswith("diff"): m = re.search(r"a/(.*?) *b/", line) udiff.append((":", m.group(1) if m else "")) nskip = 3 elif line.startswith("@@"): m = re.search(r"-(\d+),(\d+) *\+(\d+),(\d+)", line) spans = tuple(map(int, m.groups())) if m else (0, 0, 0, 0) udiff.append(("@", spans)) elif line.startswith(" "): udiff.append((" ", line[1:])) elif line.startswith("-"): udiff.append(("-", line[1:])) elif line.startswith("+"): udiff.append(("+", line[1:])) return udiff
def is_versioned (self, path): # Base override. root, path = self._gitroot(path) if not path: return True res = collect_system(["git", "status"], wdir=root, env=self._env) rx = re.compile(r"untracked.*?:", re.I) m = rx.search(res[0]) if m: for line in res[0][m.end():].split("\n"): line = line.lstrip("#").strip() if line == path: return False return True
def diff (self, path, rev1=None, rev2=None): # Base override. if rev1 is not None and rev2 is not None: rspec = "-r %s:%s" % (rev1, rev2) elif rev1 is not None: rspec = "-r %s" % rev1 elif rev2 is not None: raise PologyError( _("@info \"Subversion\" is a version control system", "Subversion cannot diff from working copy " "to a named revision.")) else: rspec = "" res = collect_system(["svn", "diff", path, rspec], env=self._env) if res[-1] != 0: warning(_("@info", "Subversion reports it cannot diff path '%(path)s':\n" "%(msg)s", path=path, msg=res[1])) return [] udiff = [] nskip = 0 for line in res[0].split("\n"): if nskip > 0: nskip -= 1 continue if line.startswith("Index:"): udiff.append((":", line[line.find(":") + 1:].strip())) nskip = 3 elif line.startswith("@@"): m = re.search(r"-(\d+),(\d+) *\+(\d+),(\d+)", line) spans = tuple(map(int, m.groups())) if m else (0, 0, 0, 0) udiff.append(("@", spans)) elif line.startswith(" "): udiff.append((" ", line[1:])) elif line.startswith("-"): udiff.append(("-", line[1:])) elif line.startswith("+"): udiff.append(("+", line[1:])) return udiff
def is_clear (self, path): # Base override. root, path = self._gitroot(path) res = collect_system(["git", "status", path], wdir=root, env=self._env) rx = re.compile(r"\bmodified:\s*(\S.*)", re.I) for line in res[0].split("\n"): m = rx.search(line) if m: mpath = m.group(1) if os.path.isfile(path): if mpath == path: return False else: if not path or mpath[len(path):].startswith(os.path.sep): return False return True
def add (self, paths, repadd=False): # Base override. if isinstance(paths, basestring): paths = [paths] if not paths: return True root, paths = self._gitroot(paths) success = True apaths = [] for path in paths: if collect_system(["git", "add", path], wdir=root)[2] != 0: success = False break apaths.append(path) return success if not repadd else [success, apaths]
def add (self, paths, repadd=False): # Base override. if isinstance(paths, basestring): paths = [paths] if not paths: return True tmppath = _temp_paths_file(paths) res = collect_system(["svn", "add", "--force", "--parents", "--targets", tmppath], env=self._env) success = (res[2] == 0) os.remove(tmppath) if repadd: apaths = [] for line in res[0].split("\n"): if line.startswith("A"): apaths.append(line[1:].strip()) return success, apaths else: return success
def main(): locale.setlocale(locale.LC_ALL, "") # Get defaults for command line options from global config. cfgsec = pology_config.section("posieve") def_do_skip = cfgsec.boolean("skip-on-error", True) def_msgfmt_check = cfgsec.boolean("msgfmt-check", False) def_skip_obsolete = cfgsec.boolean("skip-obsolete", False) # Setup options and parse the command line. usage = _("@info command usage", "%(cmd)s [OPTIONS] SIEVE [POPATHS...]", cmd="%prog") desc = _( "@info command description", "Apply sieves to PO paths, which may be either single PO files or " "directories to search recursively for PO files. " "Some of the sieves only examine PO files, while others " "modify them as well. " "The first non-option argument is the sieve name; " "a list of several comma-separated sieves can be given too.") ver = _("@info command version", u"%(cmd)s (Pology) %(version)s\n" u"Copyright © 2007, 2008, 2009, 2010 " u"Chusslove Illich (Часлав Илић) <%(email)s>", cmd="%prog", version=version(), email="*****@*****.**") opars = ColorOptionParser(usage=usage, description=desc, version=ver) opars.add_option( "-a", "--announce-entry", action="store_true", dest="announce_entry", default=False, help=_("@info command line option description", "Announce that header or message is just about to be sieved.")) opars.add_option("-b", "--skip-obsolete", action="store_true", dest="skip_obsolete", default=def_skip_obsolete, help=_("@info command line option description", "Do not sieve obsolete messages.")) opars.add_option( "-c", "--msgfmt-check", action="store_true", dest="msgfmt_check", default=def_msgfmt_check, help=_("@info command line option description", "Check catalogs by %(cmd)s and skip those which do not pass.", cmd="msgfmt -c")) opars.add_option("-u", "--single-entry", metavar=_("@info command line value placeholder", "ENTRY_NUMBER"), action="store", dest="single_entry", default=0, help=_("@info command line option description", "Only perform the check on this ENTRY_NUMBER.")) opars.add_option( "--force-sync", action="store_true", dest="force_sync", default=False, help=_("@info command line option description", "Force rewriting of all messages, whether modified or not.")) opars.add_option("-H", "--help-sieves", action="store_true", dest="help_sieves", default=False, help=_("@info command line option description", "Show help for applied sieves.")) opars.add_option("--issued-params", action="store_true", dest="issued_params", default=False, help=_( "@info command line option description", "Show all issued sieve parameters " "(from command line and user configuration).")) opars.add_option("-l", "--list-sieves", action="store_true", dest="list_sieves", default=False, help=_("@info command line option description", "List available internal sieves.")) opars.add_option("--list-options", action="store_true", dest="list_options", default=False, help=_("@info command line option description", "List the names of available options.")) opars.add_option("--list-sieve-names", action="store_true", dest="list_sieve_names", default=False, help=_("@info command line option description", "List the names of available internal sieves.")) opars.add_option("--list-sieve-params", action="store_true", dest="list_sieve_params", default=False, help=_("@info command line option description", "List the parameters known to issued sieves.")) opars.add_option("-m", "--output-modified", metavar=_("@info command line value placeholder", "FILE"), action="store", dest="output_modified", default=None, help=_("@info command line option description", "Output names of modified files into FILE.")) opars.add_option("--no-skip", action="store_false", dest="do_skip", default=def_do_skip, help=_( "@info command line option description", "Do not try to skip catalogs which signal errors.")) opars.add_option("--no-sync", action="store_false", dest="do_sync", default=True, help=_("@info command line option description", "Do not write any modifications to catalogs.")) opars.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help=_( "@info command line option description", "Do not display any progress info " "(does not influence sieves themselves).")) opars.add_option("-s", metavar=_("@info command line value placeholder", "NAME[:VALUE]"), action="append", dest="sieve_params", default=[], help=_("@info command line option description", "Pass a parameter to sieves.")) opars.add_option( "-S", metavar=_("@info command line value placeholder", "NAME[:VALUE]"), action="append", dest="sieve_no_params", default=[], help=_( "@info command line option description", "Remove a parameter to sieves " "(e.g. if it was issued through user configuration).")) opars.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help=_("@info command line option description", "Output more detailed progress information.")) add_cmdopt_filesfrom(opars) add_cmdopt_incexc(opars) add_cmdopt_colors(opars) (op, free_args) = opars.parse_args(str_to_unicode(sys.argv[1:])) if op.list_options: report(list_options(opars)) sys.exit(0) if len(free_args) < 1 and not (op.list_sieves or op.list_sieve_names): error(_("@info", "No sieve to apply given.")) op.raw_sieves = [] op.raw_paths = [] if len(free_args) > 2 and op.single_entry != 0: error( _("@info", "With single entry mode, you can only give one input file.")) if len(free_args) >= 1: op.raw_sieves = free_args[0] op.raw_paths = free_args[1:] # Could use some speedup. try: import psyco psyco.full() except ImportError: pass set_coloring_globals(ctype=op.coloring_type, outdep=(not op.raw_colors)) # Dummy-set all internal sieves as requested if sieve listing required. sieves_requested = [] if op.list_sieves or op.list_sieve_names: # Global sieves. modpaths = glob.glob(os.path.join(datadir(), "sieve", "[a-z]*.py")) modpaths.sort() for modpath in modpaths: sname = os.path.basename(modpath)[:-3] # minus .py sname = sname.replace("_", "-") sieves_requested.append(sname) # Language-specific sieves. modpaths = glob.glob( os.path.join(datadir(), "lang", "*", "sieve", "[a-z]*.py")) modpaths.sort() for modpath in modpaths: sname = os.path.basename(modpath)[:-3] # minus .py sname = sname.replace("_", "-") lang = os.path.basename(os.path.dirname(os.path.dirname(modpath))) sieves_requested.append(lang + ":" + sname) # No need to load and setup sieves if only listing sieve names requested. if op.list_sieve_names: report("\n".join(sieves_requested)) sys.exit(0) # Load sieve modules from supplied names in the command line. if not sieves_requested: sieves_requested = op.raw_sieves.split(",") sieve_modules = [] for sieve_name in sieves_requested: # Resolve sieve file. if not sieve_name.endswith(".py"): # One of internal sieves. if ":" in sieve_name: # Language-specific internal sieve. lang, name = sieve_name.split(":") sieve_path_base = os.path.join("lang", lang, "sieve", name) else: sieve_path_base = os.path.join("sieve", sieve_name) sieve_path_base = sieve_path_base.replace("-", "_") + ".py" sieve_path = os.path.join(datadir(), sieve_path_base) else: # Sieve name is its path. sieve_path = sieve_name try: sieve_file = open(unicode_to_str(sieve_path)) # ...unicode_to_str because of exec below. except IOError: error(_("@info", "Cannot load sieve '%(file)s'.", file=sieve_path)) # Load file into new module. sieve_mod_name = "sieve_" + str(len(sieve_modules)) sieve_mod = imp.new_module(sieve_mod_name) exec sieve_file in sieve_mod.__dict__ sieve_file.close() sys.modules[sieve_mod_name] = sieve_mod # to avoid garbage collection sieve_modules.append((sieve_name, sieve_mod)) if not hasattr(sieve_mod, "Sieve"): error( _("@info", "Module '%(file)s' does not define %(classname)s class.", file=sieve_path, classname="Sieve")) # Setup sieves (description, known parameters...) pp = ParamParser() snames = [] for name, mod in sieve_modules: scview = pp.add_subcmd(name) if hasattr(mod, "setup_sieve"): mod.setup_sieve(scview) snames.append(name) # If info on sieves requested, report and exit. if op.list_sieves: report(_("@info", "Available internal sieves:")) report(pp.listcmd(snames)) sys.exit(0) elif op.list_sieve_params: params = set() for scview in pp.cmdviews(): params.update(scview.params(addcol=True)) report("\n".join(sorted(params))) sys.exit(0) elif op.help_sieves: report(_("@info", "Help for sieves:")) report("") report(pp.help(snames)) sys.exit(0) # Prepare sieve parameters for parsing. sieve_params = list(op.sieve_params) # - append paramaters according to configuration sieve_params.extend(read_config_params(pp.cmdviews(), sieve_params)) # - remove paramaters according to command line if op.sieve_no_params: sieve_params_mod = [] for parspec in sieve_params: if parspec.split(":", 1)[0] not in op.sieve_no_params: sieve_params_mod.append(parspec) sieve_params = sieve_params_mod # If assembly of issued parameters requested, report and exit. if op.issued_params: escparams = [] for parspec in sieve_params: if ":" in parspec: param, value = parspec.split(":", 1) escparam = "%s:%s" % (param, escape_sh(value)) else: escparam = parspec escparams.append(escparam) fmtparams = " ".join(["-s%s" % x for x in sorted(escparams)]) if fmtparams: report(fmtparams) sys.exit(0) # Parse sieve parameters. sparams, nacc_params = pp.parse(sieve_params, snames) if nacc_params: error( _("@info", "Parameters not accepted by any of issued subcommands: " "%(paramlist)s.", paramlist=format_item_list(nacc_params))) # ======================================== # FIXME: Think of something less ugly. # Add as special parameter to each sieve: # - root paths from which the catalogs are collected # - whether destination independent coloring is in effect # - test function for catalog selection root_paths = [] if op.raw_paths: root_paths.extend(op.raw_paths) if op.files_from: for ffpath in op.files_from: root_paths.extend(collect_paths_from_file(ffpath)) if not op.raw_paths and not op.files_from: root_paths = ["."] is_cat_included = build_path_selector(incnames=op.include_names, incpaths=op.include_paths, excnames=op.exclude_names, excpaths=op.exclude_paths) for p in sparams.values(): p.root_paths = root_paths p.raw_colors = op.raw_colors p.is_cat_included = is_cat_included # ======================================== # Create sieves. sieves = [] for name, mod in sieve_modules: sieves.append(mod.Sieve(sparams[name])) # Get the message monitoring indicator from the sieves. # Monitor unless all sieves have requested otherwise. use_monitored = False for sieve in sieves: if getattr(sieve, "caller_monitored", True): use_monitored = True break if op.verbose and not use_monitored: report(_("@info:progress", "--> Not monitoring messages.")) # Get the sync indicator from the sieves. # Sync unless all sieves have requested otherwise, # and unless syncing is disabled globally in command line. do_sync = False for sieve in sieves: if getattr(sieve, "caller_sync", True): do_sync = True break if not op.do_sync: do_sync = False if op.verbose and not do_sync: report(_("@info:progress", "--> Not syncing after sieving.")) # Open in header-only mode if no sieve has message processor. # Categorize sieves by the presence of message/header processors. use_headonly = True header_sieves = [] header_sieves_last = [] message_sieves = [] for sieve in sieves: if hasattr(sieve, "process"): use_headonly = False message_sieves.append(sieve) if hasattr(sieve, "process_header"): header_sieves.append(sieve) if hasattr(sieve, "process_header_last"): header_sieves_last.append(sieve) if op.verbose and use_headonly: report(_("@info:progress", "--> Opening catalogs in header-only mode.")) # Collect catalog paths. fnames = collect_paths_cmdline(rawpaths=op.raw_paths, incnames=op.include_names, incpaths=op.include_paths, excnames=op.exclude_names, excpaths=op.exclude_paths, filesfrom=op.files_from, elsecwd=True, respathf=collect_catalogs, abort=True) if op.do_skip: errwarn = warning errwarn_on_msg = warning_on_msg else: errwarn = error errwarn_on_msg = error_on_msg # Prepare inline progress indicator. if not op.quiet: update_progress = init_file_progress(fnames, addfmt=t_("@info:progress", "Sieving: %(file)s")) # Sieve catalogs. modified_files = [] for fname in fnames: if op.verbose: report(_("@info:progress", "Sieving %(file)s...", file=fname)) elif not op.quiet: update_progress(fname) if op.msgfmt_check: d1, oerr, ret = collect_system( ["msgfmt", "-o", "/dev/null", "-c", fname]) if ret != 0: oerr = oerr.strip() errwarn( _("@info:progress", "%(file)s: %(cmd)s check failed:\n" "%(msg)s", file=fname, cmd="msgfmt -c", msg=oerr)) warning( _("@info:progress", "Skipping catalog due to syntax check failure.")) continue try: cat = Catalog(fname, monitored=use_monitored, headonly=use_headonly, single_entry=int(op.single_entry)) except CatalogSyntaxError, e: errwarn( _("@info:progress", "%(file)s: Parsing failed: %(msg)s", file=fname, msg=e)) warning( _("@info:progress", "Skipping catalog due to parsing failure.")) continue skip = False # First run all header sieves. if header_sieves and op.announce_entry: report( _("@info:progress", "Sieving header of %(file)s...", file=fname)) for sieve in header_sieves: try: ret = sieve.process_header(cat.header, cat) except SieveCatalogError, e: errwarn( _("@info:progress", "%(file)s:header: Sieving failed: %(msg)s", file=fname, msg=e)) skip = True break if ret not in (None, 0): break