D2872: wireproto: define human output side channel frame

2018-03-24 Thread yuja (Yuya Nishihara)
yuja added inline comments.

INLINE COMMENTS

> wireprotoframing.py:318
> +# Formatting string must be UTF-8.
> +formatting = formatting.decode(r'utf-8', r'replace').encode(r'utf-8')
> +

It's probably better to require everything in ASCII if `formatting` is supposed 
to be fed to `_()`.

It's a disaster to mix utf-8 bytes and local-encoding bytes in codebase.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2872

To: indygreg, #hg-reviewers, durin42
Cc: yuja, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2872: wireproto: define human output side channel frame

2018-03-21 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG0a6c5cc09a88: wireproto: define human output side channel 
frame (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2872?vs=7148=7236

REVISION DETAIL
  https://phab.mercurial-scm.org/D2872

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -67,6 +67,109 @@
 ffs(b'1 command-data eos %s' % data.getvalue()),
 ])
 
+def testtextoutputexcessiveargs(self):
+"""At most 255 formatting arguments are allowed."""
+with self.assertRaisesRegexp(ValueError,
+ 'cannot use more than 255 formatting'):
+args = [b'x' for i in range(256)]
+list(framing.createtextoutputframe(1, [(b'bleh', args, [])]))
+
+def testtextoutputexcessivelabels(self):
+"""At most 255 labels are allowed."""
+with self.assertRaisesRegexp(ValueError,
+ 'cannot use more than 255 labels'):
+labels = [b'l' for i in range(256)]
+list(framing.createtextoutputframe(1, [(b'bleh', [], labels)]))
+
+def testtextoutputformattingstringtype(self):
+"""Formatting string must be bytes."""
+with self.assertRaisesRegexp(ValueError, 'must use bytes formatting '):
+list(framing.createtextoutputframe(1, [
+(b'foo'.decode('ascii'), [], [])]))
+
+def testtextoutputargumentbytes(self):
+with self.assertRaisesRegexp(ValueError, 'must use bytes for 
argument'):
+list(framing.createtextoutputframe(1, [
+(b'foo', [b'foo'.decode('ascii')], [])]))
+
+def testtextoutputlabelbytes(self):
+with self.assertRaisesRegexp(ValueError, 'must use bytes for labels'):
+list(framing.createtextoutputframe(1, [
+(b'foo', [], [b'foo'.decode('ascii')])]))
+
+def testtextoutputtoolongformatstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'formatting string cannot be longer 
than'):
+list(framing.createtextoutputframe(1, [
+(b'x' * 65536, [], [])]))
+
+def testtextoutputtoolongargumentstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'argument string cannot be longer than'):
+list(framing.createtextoutputframe(1, [
+(b'bleh', [b'x' * 65536], [])]))
+
+def testtextoutputtoolonglabelstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'label string cannot be longer than'):
+list(framing.createtextoutputframe(1, [
+(b'bleh', [], [b'x' * 65536])]))
+
+def testtextoutput1simpleatom(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], [])]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x00\x00foo'),
+])
+
+def testtextoutput2simpleatoms(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], []),
+(b'bar', [], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x00\x00foo\x03\x00\x00\x00bar'),
+])
+
+def testtextoutput1arg(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s', [b'val1'], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x06\x00\x00\x01\x04\x00foo %sval1'),
+])
+
+def testtextoutput2arg(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s %s', [b'val', b'value'], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x09\x00\x00\x02\x03\x00\x05\x00'
+br'foo %s %svalvalue'),
+])
+
+def testtextoutput1label(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], [b'label']),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x01\x00\x05foolabel'),
+])
+
+def testargandlabel(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s', [b'arg'], [b'label']),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x06\x00\x01\x01\x05\x03\x00foo 
%slabelarg'),
+])
+
 class ServerReactorTests(unittest.TestCase):
 def _sendsingleframe(self, reactor, s):
 results = list(sendframes(reactor, [ffs(s)]))
diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- 

D2872: wireproto: define human output side channel frame

2018-03-19 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 7148.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2872?vs=7057=7148

REVISION DETAIL
  https://phab.mercurial-scm.org/D2872

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -67,6 +67,109 @@
 ffs(b'1 command-data eos %s' % data.getvalue()),
 ])
 
+def testtextoutputexcessiveargs(self):
+"""At most 255 formatting arguments are allowed."""
+with self.assertRaisesRegexp(ValueError,
+ 'cannot use more than 255 formatting'):
+args = [b'x' for i in range(256)]
+list(framing.createtextoutputframe(1, [(b'bleh', args, [])]))
+
+def testtextoutputexcessivelabels(self):
+"""At most 255 labels are allowed."""
+with self.assertRaisesRegexp(ValueError,
+ 'cannot use more than 255 labels'):
+labels = [b'l' for i in range(256)]
+list(framing.createtextoutputframe(1, [(b'bleh', [], labels)]))
+
+def testtextoutputformattingstringtype(self):
+"""Formatting string must be bytes."""
+with self.assertRaisesRegexp(ValueError, 'must use bytes formatting '):
+list(framing.createtextoutputframe(1, [
+(b'foo'.decode('ascii'), [], [])]))
+
+def testtextoutputargumentbytes(self):
+with self.assertRaisesRegexp(ValueError, 'must use bytes for 
argument'):
+list(framing.createtextoutputframe(1, [
+(b'foo', [b'foo'.decode('ascii')], [])]))
+
+def testtextoutputlabelbytes(self):
+with self.assertRaisesRegexp(ValueError, 'must use bytes for labels'):
+list(framing.createtextoutputframe(1, [
+(b'foo', [], [b'foo'.decode('ascii')])]))
+
+def testtextoutputtoolongformatstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'formatting string cannot be longer 
than'):
+list(framing.createtextoutputframe(1, [
+(b'x' * 65536, [], [])]))
+
+def testtextoutputtoolongargumentstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'argument string cannot be longer than'):
+list(framing.createtextoutputframe(1, [
+(b'bleh', [b'x' * 65536], [])]))
+
+def testtextoutputtoolonglabelstring(self):
+with self.assertRaisesRegexp(ValueError,
+ 'label string cannot be longer than'):
+list(framing.createtextoutputframe(1, [
+(b'bleh', [], [b'x' * 65536])]))
+
+def testtextoutput1simpleatom(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], [])]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x00\x00foo'),
+])
+
+def testtextoutput2simpleatoms(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], []),
+(b'bar', [], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x00\x00foo\x03\x00\x00\x00bar'),
+])
+
+def testtextoutput1arg(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s', [b'val1'], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x06\x00\x00\x01\x04\x00foo %sval1'),
+])
+
+def testtextoutput2arg(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s %s', [b'val', b'value'], []),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x09\x00\x00\x02\x03\x00\x05\x00'
+br'foo %s %svalvalue'),
+])
+
+def testtextoutput1label(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo', [], [b'label']),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x03\x00\x01\x00\x05foolabel'),
+])
+
+def testargandlabel(self):
+val = list(framing.createtextoutputframe(1, [
+(b'foo %s', [b'arg'], [b'label']),
+]))
+
+self.assertEqual(val, [
+ffs(br'1 text-output 0 \x06\x00\x01\x01\x05\x03\x00foo 
%slabelarg'),
+])
+
 class ServerReactorTests(unittest.TestCase):
 def _sendsingleframe(self, reactor, s):
 results = list(sendframes(reactor, [ffs(s)]))
diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py
--- a/mercurial/wireprotoframing.py
+++ b/mercurial/wireprotoframing.py
@@ -27,13 +27,15 @@
 FRAME_TYPE_COMMAND_DATA = 0x03
 FRAME_TYPE_BYTES_RESPONSE = 0x04
 

D2872: wireproto: define human output side channel frame

2018-03-14 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Currently, the SSH protocol delivers output tailored for people over
  the stderr file descriptor. The HTTP protocol doesn't have this
  file descriptor (because it only has an input and output pipe). So
  it encodes textual output intended for humans within the protocol
  responses. So response types have a facility for capturing output
  to be printed to users. Some don't. And sometimes the implementation
  of how that output is conveyed is super hacky.
  
  On top of that, bundle2 has an "output" part that is used to store
  output that should be printed when this part is encountered.
  bundle2 also has the concept of "interrupt" chunks, which can be
  used to signal that the regular bundle2 stream is to be
  preempted by an out-of-band part that should be processed immediately.
  This "interrupt" part can be an "output" part and can be used to
  print data on the receiver.
  
  The status quo is inconsistent and insane. We can do better.
  
  This commit introduces a dedicated frame type on the frame-based
  protocol for denoting textual data that should be printed on the
  receiver. This frame type effectively constitutes a side-channel
  by which textual data can be printed on the receiver without
  interfering with other in-progress transmissions, such as the
  transmission of command responses.
  
  But wait - there's more! Previous implementations that transferred
  textual data basically instructed the client to "print these bytes."
  This suffered from a few problems.
  
  First, the text data that was transmitted and eventually printed
  originated from a server with a specic i18n configuration. This
  meant that clients would see text using whatever the i18n settings
  were on the server. Someone in France could connect to a server in
  Japan and see unlegible Japanese glyphs - or maybe even mojibake.
  
  Second, the normalization of all text data originated on servers
  resulted in the loss of the ability to apply formatting to that
  data. Local Mercurial clients can apply specific formatting
  settings to individual atoms of text. For example, a revision can
  be colored differently from a commit message. With data over the
  wire, the potential for this rich formatting was lost. The best you
  could do (without parsing the text to be printed), was apply a
  universal label to it and e.g. color it specially.
  
  The new mechanism for instructing the peer to print data does
  not have these limitations.
  
  Frames instructing the peer to print text are composed of a
  formatting string plus arguments. In other words, receivers can
  plug the formatting string into the i18n database to see if a local
  translation is available. In addition, each atom being instructed
  to print has a series of "labels" associated with it. These labels
  can be mapped to the Mercurial UI's labels so locally configured
  coloring, styling, etc settings can be applied.
  
  What this all means is that textual messages originating on servers
  can be localized on the client and richly formatted, all while
  respecting the client's settings. This is slightly more complicated
  than "print these bytes." But it is vastly more user friendly.
  
  FWIW, I'm not aware of other protocols that attempt to encode
  i18n and textual styling in this manner. You could lobby the
  claim that this feature is over-engineered. However, if I were to
  sit in the shoes of a non-English speaker learning how to use
  version control, I think I would *love* this feature because
  it would enable me to see richly formatted text in my chosen
  locale.
  
  Anyway, we only implement support for encoding frames of this
  type and basic tests for that encoding. We'll still need to
  hook up the server and its ui instance to emit these frames.
  I recognize this feature may be a bit more controversial than
  other aspects of the wire protocol because it is a bit
  "radical." So I'd figured I'd start small to test the waters and
  see if others feel this feature is worthwhile.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2872

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoframing.py
  tests/test-wireproto-serverreactor.py

CHANGE DETAILS

diff --git a/tests/test-wireproto-serverreactor.py 
b/tests/test-wireproto-serverreactor.py
--- a/tests/test-wireproto-serverreactor.py
+++ b/tests/test-wireproto-serverreactor.py
@@ -67,6 +67,109 @@
 ffs(b'1 command-data eos %s' % data.getvalue()),
 ])
 
+def testtextoutputexcessiveargs(self):
+"""At most 255 formatting arguments are allowed."""
+with self.assertRaisesRegexp(ValueError,
+ 'cannot use more than 255 formatting'):
+args = [b'x' for i in range(256)]
+list(framing.createtextoutputframe(1,