Esempio n. 1
0
def diff_hdrs(hdr1, hdr2, vpath1, vpath2, hmsgctxt, ecat, colorize):

    hmsg1, hmsg2 = [
        x and MessageUnsafe(x.to_msg()) or None for x in (hdr1, hdr2)
    ]

    ehmsg = hmsg2 and MessageUnsafe(hmsg2) or None
    ehmsg, dr = msg_ediff(hmsg1,
                          hmsg2,
                          emsg=ehmsg,
                          ecat=ecat,
                          colorize=colorize,
                          diffr=True)
    if dr == 0.0:
        # Revert to empty message if no difference between headers.
        ehmsg = MessageUnsafe()

    # Add visual paths as old/new segments into msgid.
    vpaths = [vpath1, vpath2]
    # Always use slashes as path separator, for portability of ediffs.
    vpaths = [x.replace(os.path.sep, "/") for x in vpaths]
    ehmsg.msgid = u"- %s\n+ %s" % tuple(vpaths)
    # Add trailing newline if msgstr has it, again to appease msgfmt.
    if ehmsg.msgstr[0].endswith("\n"):
        ehmsg.msgid += "\n"

    # Add context identifying the diffed message as header.
    ehmsg.msgctxt = hmsgctxt

    # Add conspicuous separator at the top of the header.
    ehmsg.manual_comment.insert(0, u"=" * 76)

    return ehmsg, dr > 0.0
Esempio n. 2
0
def _add_msg_diff(msg1, msg2, ecat, colorize, fnsyn=None):

    # Skip diffing if old and new messages are "same".
    if msg1 and msg2 and msg1.inv == msg2.inv:
        return 0

    # Create messages for special pairings.
    msg1_s, msg2_s = _create_special_diff_pair(msg1, msg2)

    # Create the diff.
    tmsg = msg2 or msg1
    emsg = msg2_s or msg1_s
    if emsg is tmsg:
        emsg = MessageUnsafe(tmsg)
    emsg = msg_ediff(msg1_s, msg2_s, emsg=emsg, ecat=ecat, colorize=colorize)

    # Add to the diff catalog.
    if fnsyn is None:
        ecat.add(emsg, len(ecat))
    else:
        ecat.add(emsg, srefsyn=fnsyn)

    return 1
Esempio n. 3
0
def hybdl(path, path0, accnohyb=False):

    cat = Catalog(path)
    cat0 = Catalog(path0, monitored=False)

    nhybridized = 0
    nstopped = 0
    for msg in cat:

        if "no-hybdl" in manc_parse_flag_list(msg, "|"):
            continue

        # Unembed diff if message was diffed for review.
        # Replace ediff with manual review flag.
        diffed = False
        for flag in msg.flag:
            if flag.startswith("ediff"):
                msg.flag.remove(flag)
                diffed = True
        if diffed:
            msg_ediff_to_new(msg, msg)
            msg.flag.add(u"reviewed")

        # Fetch original message.
        msg0 = cat0.get(msg)
        if msg0 is None:
            warning_on_msg(
                _("@info", "Message does not exist in the original catalog."),
                msg, cat)
            nstopped += 1
            continue
        if len(msg.msgstr) != len(msg0.msgstr):
            warning_on_msg(
                _(
                    "@info", "Number of translations not same as in "
                    "the original message."), msg, cat)
            nstopped += 1
            continue
        if msg.msgstr == msg0.msgstr:
            # No changes, nothing new to hybridize.
            continue

        # Hybridize translation.
        textsh = []
        textshinv = []
        for text0, text in zip(msg0.msgstr, msg.msgstr):
            texth = tohi(text0, text, parthyb=True)
            textsh.append(texth)
            if not accnohyb:
                texthinv = tohi(text, text0, parthyb=True)
                textshinv.append(texthinv)
        if accnohyb or textsh == textshinv:
            for i, texth in zip(range(len(msg.msgstr)), textsh):
                msg.msgstr[i] = texth
            nhybridized += 1
        else:
            nstopped += 1
            msgh = MessageUnsafe(msg)
            msgh.msgstr = textsh
            msghinv = MessageUnsafe(msg)
            msghinv.msgstr = textshinv
            msg_ediff(msghinv, msgh, emsg=msgh, colorize=True)
            report_msg_content(msgh, cat, delim=("-" * 20))

    if nstopped == 0:
        if cat.sync():
            report("! %s (%d)" % (path, nhybridized))
    else:
        warning(
            n_("@info", "%(num)d message in '%(file)s' cannot be "
               "cleanly hybridized.",
               "%(num)d messages in '%(file)s' cannot be "
               "cleanly hybridized.",
               num=nstopped,
               file=path))
        nhybridized = 0

    return nhybridized
Esempio n. 4
0
def msg_apply_diff(cat, emsg, ecat, pmsgkeys, striplets):

    msg1, msg2, msg1_s, msg2_s = resolve_diff_pair(emsg)

    # Try to select existing message from the original messages.
    # Order is important, should try first new, then old
    # (e.g. if an old fuzzy was resolved to new after diff was made).
    msg = None
    if msg2 and msg2 in cat:
        msg = cat[msg2]
    elif msg1 and msg1 in cat:
        msg = cat[msg1]

    patch_specs = []

    # Try to apply the patch.
    if msg_patchable(msg, msg1, msg2):
        # Patch can be directly applied.
        if msg1 and msg2:
            if msg.key not in pmsgkeys:
                typ = _pt_merge
                pos = cat.find(msg)
                pmsgkeys.add(msg.key)
            else:
                typ = _pt_insert
                pos, weight = cat.insertion_inquiry(msg2)
        elif msg2:  # patch adds a message
            if msg:
                typ = _pt_merge
                pos = cat.find(msg)
                pmsgkeys.add(msg.key)
            else:
                typ = _pt_insert
                pos, weight = cat.insertion_inquiry(msg2)
        elif msg1:  # patch removes a message
            if msg:
                typ = _pt_remove
                pos = cat.find(msg)
                pmsgkeys.add(msg.key)
            else:
                typ = _pt_remove
                pos = None  # no position to remove from
        else:
            # Cannot happen.
            error_on_msg(
                _(
                    "@info", "Neither the old nor the new message "
                    "in the diff is indicated to exist."), emsg, ecat)
        patch_specs.append(
            (emsg, _flag_ediff, typ, pos, msg1, msg2, msg1_s, msg2_s))
    else:
        # Patch cannot be applied directly,
        # try to split into old-to-current and current-to-new diffs.
        split_found = False
        if callable(striplets):
            striplets = striplets()  # delayed creation of splitting triplets
        for i in range(len(striplets)):
            m1_t, m1_ts, m2_t, m2_ts, m_t, m_ts1, m_ts2 = striplets[i]
            if msg1.inv == m1_t.inv and msg2.inv == m2_t.inv:
                striplets.pop(i)  # remove to not slow further searches
                split_found = True
                break
        if split_found:
            # Construct new corresponding diffs.
            em_1c = msg_ediff(m1_ts, m_ts1, emsg=MessageUnsafe(m_t))
            em_c2 = msg_ediff(m_ts2, m2_ts, emsg=MessageUnsafe(m2_t))
            # Current-to-new can be merged or inserted,
            # and old-to-current is then inserted just before it.
            if m_t.key not in pmsgkeys:
                typ = _pt_merge
                pos = cat.find(m_t)
                pmsgkeys.add(m_t.key)
            else:
                typ = _pt_insert
                pos, weight = cat.insertion_inquiry(m2_t)
            # Order of adding patch specs here important for rejects file.
            patch_specs.append((em_1c, _flag_ediff_to_cur, _pt_insert, pos,
                                m1_t, m_t, m1_ts, m_ts1))
            patch_specs.append(
                (em_c2, _flag_ediff_to_new, typ, pos, m_t, m2_t, m_ts2, m2_ts))

    # The patch is totally rejected.
    # Will be inserted if reembedding requested, so compute insertion.
    if not patch_specs:
        typ = _pt_insert
        if msg2 is not None:
            pos, weight = cat.insertion_inquiry(msg2)
        else:
            pos = len(cat)
        patch_specs.append(
            (emsg, _flag_ediff_no_match, typ, pos, msg1, msg2, msg1_s, msg2_s))

    return patch_specs
Esempio n. 5
0
def patch_messages(cat, emsgs, ecat, options):

    # It may happen that a single message from original catalog
    # is paired with more than one from the diff
    # (e.g. single old translated message going into two new fuzzy).
    # Therefore paired messages must be tracked, to know if patched
    # message can be merged into the existing, or it must be inserted.
    pmsgkeys = set()

    # Triplets for splitting directly unapplicable patches into two.
    # Delay building of triplets until needed for the first time.
    striplets_pack = [None]

    def striplets():
        if striplets_pack[0] is None:
            striplets_pack[0] = build_splitting_triplets(emsgs, cat, options)
        return striplets_pack[0]

    # Check whether diffs apply, and where and how if they do.
    rejected_emsgs_flags = []
    patch_specs = []
    for emsg in emsgs:
        pspecs = msg_apply_diff(cat, emsg, ecat, pmsgkeys, striplets)
        for pspec in pspecs:
            emsg_m, flag = pspec[:2]
            if flag == _flag_ediff or options.embed:
                patch_specs.append(pspec)
            if flag != _flag_ediff:
                rejected_emsgs_flags.append((emsg_m, flag))

    # Sort accepted patches by position of application.
    patch_specs.sort(key=lambda x: x[3])

    # Add accepted patches to catalog.
    incpos = 0
    for emsg, flag, typ, pos, msg1, msg2, msg1_s, msg2_s in patch_specs:
        if pos is not None:
            pos += incpos

        if options.embed:
            # Embedded diff may conflict one of the messages in catalog.
            # Make a new diff of special messages,
            # and embed them either into existing message in catalog,
            # or into new message.
            if typ == _pt_merge:
                tmsg = cat[pos]
                tpos = pos
            else:
                tmsg = MessageUnsafe(msg2 or {})
                tpos = None
            emsg = msg_ediff(msg1_s, msg2_s, emsg=tmsg, ecat=cat, eokpos=tpos)

        if 0: pass
        elif typ == _pt_merge:
            if not options.embed:
                cat[pos].set_inv(msg2)
            else:
                cat[pos].flag.add(flag)
        elif typ == _pt_insert:
            if not options.embed:
                cat.add(Message(msg2), pos)
            else:
                cat.add(Message(emsg), pos)
                cat[pos].flag.add(flag)
            incpos += 1
        elif typ == _pt_remove:
            if pos is None:
                continue
            if not options.embed:
                cat.remove(pos)
                incpos -= 1
            else:
                cat[pos].flag.add(flag)
        else:
            error_on_msg(_("@info", "Unknown patch type %(type)s.", type=typ),
                         emsg, ecat)

    return rejected_emsgs_flags
Esempio n. 6
0
def apply_ediff(op):

    # Read the ediff PO.
    dummy_stream_path = "<stdin>"
    if op.input:
        if not os.path.isfile(op.input):
            error(
                _("@info",
                  "Path '%(path)s' is not a file or does not exist.",
                  path=op.input))
        edfpath = op.input
        readfh = None
    else:
        edfpath = dummy_stream_path
        readfh = sys.stdin
    try:
        ecat = Catalog(edfpath, monitored=False, readfh=readfh)
    except:
        error(
            _("@info ediff is shorthand for \"embedded difference\"",
              "Error reading ediff '%(file)s'.",
              file=edfpath))

    # Split ediff by diffed catalog into original and new file paths,
    # header message, and ordinary messages.
    hmsgctxt = ecat.header.get_field_value(EDST.hmsgctxt_field)
    if hmsgctxt is None:
        error(
            _("@info",
              "Header field '%(field)s' is missing in the ediff.",
              field=EDST.hmsgctxt_field))
    edsplits = []
    cehmsg = None
    smsgid = u"\x00"
    ecat.add_last(MessageUnsafe(dict(msgctxt=hmsgctxt,
                                     msgid=smsgid)))  # sentry
    for emsg in ecat:
        if emsg.msgctxt == hmsgctxt:
            if cehmsg:
                # Record previous section.
                edsplits.append((fpaths, cehmsg, cemsgs))
                if emsg.msgid == smsgid:  # end sentry, avoid parsing below
                    break

            # Mine original and new file paths out of header.
            fpaths = []
            for fpath in emsg.msgid.split("\n")[:2]:
                # Strip leading "+ "/"- "
                fpath = fpath[2:]
                # Convert to planform path separators.
                fpath = re.sub(r"/+", os.path.sep, fpath)
                # Remove revision indicator.
                p = fpath.find(EDST.filerev_sep)
                if p >= 0:
                    fpath = fpath[:p]
                # Strip path and append directory as requested.
                if op.strip:
                    preflen = int(op.strip)
                    lst = fpath.split(os.path.sep, preflen)
                    if preflen + 1 == len(lst):
                        fpath = lst[preflen]
                    else:
                        fpath = os.path.basename(fpath)
                else:
                    fpath = os.path.basename(fpath)
                if op.directory and fpath:
                    fpath = os.path.join(op.directory, fpath)
                # All done.
                fpaths.append(fpath)

            cehmsg = emsg
            cemsgs = []
        else:
            cemsgs.append(emsg)

    # Prepare catalog for rejects and merges.
    rcat = Catalog("", create=True, monitored=False, wrapping=ecat.wrapping())
    init_ediff_header(rcat.header, hmsgctxt=hmsgctxt, extitle="rejects")

    # Apply diff to catalogs.
    for fpaths, ehmsg, emsgs in edsplits:
        # Open catalog for patching.
        fpath1, fpath2 = fpaths
        if fpath1:
            # Diff from an existing catalog, open it.
            if not os.path.isfile(fpath1):
                warning(
                    _("@info",
                      "Path '%(path)s' is not a file or does not exist, "
                      "skipping it.",
                      path=fpath1))
                continue
            try:
                cat = Catalog(fpath1)
            except:
                warning(
                    _("@info",
                      "Error reading catalog '%(file)s', skipping it.",
                      file=fpath1))
                continue
        elif fpath2:
            # New catalog added in diff, create it (or open if it exists).
            try:
                mkdirpath(os.path.dirname(fpath2))
                cat = Catalog(fpath2, create=True)
                if cat.created():
                    cat.set_wrapping(ecat.wrapping())
            except:
                if os.path.isfile(fpath2):
                    warning(
                        _("@info",
                          "Error reading catalog '%(file)s', skipping it.",
                          file=fpath1))
                else:
                    warning(
                        _("@info",
                          "Cannot create catalog '%(file)s', skipping it.",
                          file=fpath2))
                continue
        else:
            error(_("@info", "Both catalogs in ediff indicated not to exist."))

        # Do not try to patch catalog with embedded differences
        # (i.e. previously patched using -e).
        if cat.header.get_field_value(EDST.hmsgctxt_field) is not None:
            warning(
                _("@info", "Catalog '%(file)s' already contains "
                  "embedded differences, skipping it.",
                  file=cat.filename))
            continue

        # Do not try to patch catalog if the patch contains
        # unresolved split differences.
        if reduce(lambda r, x: r or _flag_ediff_to_new in x.flag, emsgs,
                  False):
            warning(
                _("@info", "Patch for catalog '%(file)s' contains unresolved "
                  "split differences, skipping it.",
                  file=cat.filename))
            continue

        # Patch the catalog.
        rejected_ehmsg = patch_header(cat, ehmsg, ecat, op)
        rejected_emsgs_flags = patch_messages(cat, emsgs, ecat, op)
        any_rejected = rejected_ehmsg or rejected_emsgs_flags
        if fpath2 or any_rejected:
            created = cat.created()
            if cat.sync():
                if not created:
                    if any_rejected and op.embed:
                        report(
                            _("@info:progress E is for \"with embedding\"",
                              "Partially patched (E): %(file)s",
                              file=cat.filename))
                    elif any_rejected:
                        report(
                            _("@info:progress",
                              "Partially patched: %(file)s",
                              file=cat.filename))
                    elif op.embed:
                        report(
                            _("@info:progress E is for \"with embedding\"",
                              "Patched (E): %(file)s",
                              file=cat.filename))
                    else:
                        report(
                            _("@info:progress",
                              "Patched: %(file)s",
                              file=cat.filename))
                else:
                    if op.embed:
                        report(
                            _("@info:progress E is for \"with embedding\"",
                              "Created (E): %(file)s",
                              file=cat.filename))
                    else:
                        report(
                            _("@info:progress",
                              "Created: %(file)s",
                              file=cat.filename))
            else:
                pass  #report("unchanged: %s" % cat.filename)
        else:
            os.unlink(fpath1)
            report(_("@info:progress", "Removed: %(file)s", file=fpath1))

        # If there were any rejects and reembedding is not in effect,
        # record the necessary to present them.
        if any_rejected and not op.embed:
            if not rejected_ehmsg:
                # Clean header diff.
                ehmsg.manual_comment = ehmsg.manual_comment[:1]
                ehmsg.msgstr[0] = u""
            rcat.add_last(ehmsg)
            for emsg, flag in rejected_emsgs_flags:
                # Reembed to avoid any conflicts.
                msg1, msg2, msg1_s, msg2_s = resolve_diff_pair(emsg)
                emsg = msg_ediff(msg1_s,
                                 msg2_s,
                                 emsg=msg2_s,
                                 ecat=rcat,
                                 enoctxt=hmsgctxt)
                if flag:
                    emsg.flag.add(flag)
                rcat.add_last(emsg)

    # If there were any rejects, write them out.
    if len(rcat) > 0:
        # Construct paths for embedded diffs of rejects.
        rsuff = "rej"
        if ecat.filename != dummy_stream_path:
            rpath = ecat.filename
            p = rpath.rfind(".")
            if p < 0:
                p = len(rpath)
            rpath = rpath[:p] + (".%s" % rsuff) + rpath[p:]
        else:
            rpath = "stdin.%s.po" % rsuff

        rcat.filename = rpath
        rcat.sync(force=True, noobsend=True)
        report(
            _(
                "@info:progress file to which rejected parts of the patch "
                "have been written to",
                "*** Rejects: %(file)s",
                file=rcat.filename))