sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.41k stars 475 forks source link

Pretty Console Output --> ascii art #14266

Closed 3f8450e1-87bf-41c6-ab53-29a0552debb3 closed 11 years ago

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago

My patch creates a simple ascii-art module to manipule several structure: list,dict,tuple, linear expression:

sage: R = NonCommutativeSymmetricFunctions(QQ).R()
sage: R[1]**5
R  + R   + R   + R    + R   + R    + R    + R     + R   + R    +
 *    **     *    ***     *     **      *    ****     *     **  
 *    *     **    *       *    **     ***    *        *     *   
 *    *     *     *      **    *      *               *    **   
 *    *     *            *                           **
 *

 R    + R     + R    + R     + R     + R
    *     ***      *      **       *    *****
   **    **        *    ***     ****
  **             ***

Apply:

Depends on #8703 Depends on #14203 Depends on #14574

CC: @nthiery @VivianePons

Component: user interface

Keywords: ascii-art

Author: Jean-Baptiste Priez

Reviewer: Volker Braun, Travis Scrimshaw

Merged: sage-5.11.beta0

Issue created by migration from https://trac.sagemath.org/ticket/14266

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago

patch

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago

Attachment: pretty_console_print-EliX-jbp.patch.gz

several examples (some not in the patch)

vbraun commented 11 years ago
comment:1

Attachment: pcp_monkey_patch.py.gz

Whats the difference between _repr_, _pretty_repr_normal_, and _pretty_repr_term_? I think there should be only one _repr_, using ascii art unconditionally if printing the object benefits from it. Why would you want to turn it off? I don't see the need for a different display handler, we aleady have one that does handle ascii art.

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:2

I implement _pretty_repr_ to introduce a special ascii-art method (like in mupad) and I prefere let the method _repr_ to allow differents use. In general case, we can copy the ouput and re-use it in terminal (it is very usefull I think). Then I put a method _pretty_repr_term_ like we have _repr_term_ for a linear expression.

I saw your handle ascii art and I try to send you a mail to merge that.... (no??).

vbraun commented 11 years ago
comment:3

I saw your email but #14040 has already been merged. What I'm saying is, there should be more integration into the existing framework and less reinventing the wheel. Also nobody wants an ugly representation. You can't copy/paste matrices either. To get an input that recreates the object, we already have

sage: m = matrix(ZZ, [[0, 0, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])
sage: m
[0 0 0]
[0 0 0]
[1 0 0]
[0 1 0]
[0 0 1]
sage: sage_input(m)
matrix(ZZ, [[0, 0, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])
vbraun commented 11 years ago
comment:4

How about a %magic to switch to the sage input representation to the displayhook, e.g.:

sage: %sage_input on
sage: m = matrix(ZZ, [[0, 0, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])
sage: m
matrix(ZZ, [[0, 0, 0], [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])

or perhaps both (if sage_input differs from _repr_)

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:5

I don't reinventing the wheel... Then I prefere a nice LaTeX representation too but with some objects the LaTeX compilation is very slow. Then thanks to the method

sage: sage_input(m)

I didn't know but... after try...

sage: R = NonCommutativeSymmetricFunctions(QQ).R()
sage: a = R[1]**3; a
R  + R   + R   + R
 *    **     *    ***
 *    *     **
 *
sage: %pcp
'Pretty Console Print' is uninstalled
sage: sage_input(a)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-10-7eccf1bd1684> in <module>()
----> 1 sage_input(a)

/home/priezjea/sage-5.8.beta4/local/lib/python2.7/site-packages/sage/misc/sage_input.pyc in sage_input(x, preparse, verify, allow_locals)
    252     if not verify:
    253         sib = SageInputBuilder(allow_locals=allow_locals, preparse=preparse)
--> 254         return sib.result(sib(x))
    255 
    256     # In verify mode, we actually compute and verify the answer with

/home/priezjea/sage-5.8.beta4/local/lib/python2.7/site-packages/sage/misc/sage_input.pyc in __call__(self, x, coerced)
    540             return SIE_literal_stringrep(self, loc_name)
    541         else:
--> 542             raise ValueError, "Can't convert %r to sage_input form"%x
    543 
    544     def preparse(self):

ValueError: Can't convert R[1, 1, 1] + R[1, 2] + R[2, 1] + R[3] to sage_input form

and

sage: R[1]**3
R[1, 1, 1] + R[1, 2] + R[2, 1] + R[3]

can be easily be copy.

So I don't say I invented something... I just purpose something to work (In my case) easily with linear combination.

vbraun commented 11 years ago
comment:6

So the bug is that NonCommutativeSymmetricFunctions doesn't define its own Sage input representation (define _sage_input_). The ascii art should be the default (and only) print representation.

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:7

I like the idea of a magic function "%sage_input on". And if every thing is ascii-art... I have some question:

1 - The _repr_ method return (and must return) a string and it is too poor to produce an efficient and usefull ascii-art output. We could image use some baseline notion or ... My opinion is an ascii-art output must be a special class like PrettyConsoleRepr or whatever which can be improve by heritage. (That is not available with string.) --> That why my opinion is we don't have to use _repr_ but anything else.

2 - How to use your code to get (easily) a list, a dict or whatever of some objects with an ascii-art output ? Your code implement only a hack for Matrix (or it was before...) no? --> If it is true my code suggests a solution (may be a bad one...)

(Sorry for my very bad english)

VivianePons commented 11 years ago
comment:8

I am a bit new on this discussion, but I want to add my contribution.

I think having _pretty_repr_ as a plus for combinatorial objects is a good idea and useful. It is not the same as _repr_.

First, you don't want to change the repr of every combinatorial objects to have pretty prints, this will just break everything every where. (I am thinking of trees, Dyck paths, partitions...). Most objects already had a way being pretty printed, it was just not centralized and could not be used in linear combinations.

I actually think having the two _repr_ and _pretty_repr_ is useful. I have started to work with Jean-Baptiste's patch and depending on what I want to do with the output, I use both the pretty prints and usal prints.

hivert commented 11 years ago
comment:9

Replying to @vbraun:

Whats the difference between _repr_, _pretty_repr_normal_, and _pretty_repr_term_? I think there should be only one _repr_, using ascii art unconditionally if printing the object benefits from it. Why would you want to turn it off? I don't see the need for a different display handler, we aleady have one that does handle ascii art.

There is definitely a need for a configurable display. In combinatorics depending on the context we really need to be able to be able to print various object in several different ways: The simplest example is partitions. Sometimes you need to see: (3,3,2,1,1) sometime you need

   ###
   ###
   ##
   #
   #

or if you are french

   #
   #
   ##
   ###
   ###

Another example is graphs. Currently we only have Graph on 6 vertices Probably many user will rather have Graph on 6 vertices with 5 edges Or to see the incidence matrix...

So I definitely see a need for configurable output.

Florent

vbraun commented 11 years ago
comment:10

I don't agree with your partition example: It should just print ascii-art (with some cutoff if the tableau gets too large) and provide a method to get the tuple-of-integers. Which you need anyways. And the French should get along with the program and get used to the same notation as the rest of the world ;-)

Seriously, we should first of all think about sane defaults that are as expressive as possible without being overly verbose. Just making everything configurable is a lame cop-out where you admit that you couldn't be bothered thinking about good defaults. If you want to see some other representation then you can always add an extra method. Why do you want a %magic to always print graphs as incidence matrix if you can just call the incidence_matrix() method?

tscrim commented 11 years ago
comment:11

I would recommend using the GlobalOptions that was setup in #13605 (you can look in partition.py or tableau.py for examples) to choose been what type of output you want on a global level. I also have some fairly generic code for ascii printing lists of arrays (it's currently sitting inside of #13872 and have been calling the specialized display functions _repr_diagram()), which is what you'd want since you'd want to call partition._repr_ to be consistent with the options specified there. I could separate that out into a separate ticket or copy/paste it here if you'd like.

hivert commented 11 years ago
comment:12

Replying to @vbraun:

I don't agree with your partition example: It should just print ascii-art (with some cutoff if the tableau gets too large) and provide a method to get the tuple-of-integers. Which you need anyways. And the French should get along with the program and get used to the same notation as the rest of the world ;-)

Unforunately, I seems that many combinatorialist agree with me. Please se the beginning of the sage/combinat/partitions.py files. There is a PartitionOptions dictionary which allows one to configure various thing including the output.

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago

new ascii art version.

That one is compatible with the Braun's displayhook.

It uses sympy to rewrite expression (or try to rewrite)::

sage: integral(exp(x^2)/(x+1), x)
⌠
⎮  ⎛ 2⎞
⎮  ⎝x ⎠
⎮ ℯ
⎮ ───── dx
⎮ x + 1
⌡

(FIXME:: For that I have to use utf-8 output for that I use a hack in sage/all_cmdline.py...)

I implement several pretty_repr for combinatorial object and plug that for free_module::

sage: R = NonCommutativeSymmetricFunctions(QQ).ribbon()
sage: Tableaux.global_options(convention="french")
sage: R[3,1,1]
R
 ***
   *
   *
sage: R[3,1,1] * R[2,1,2]
R      + R
 ***      ***
   *        *
   *        ***
   **         *
    *         **
    **
sage: F = QSym.F()
sage: F[2,1,2]
F
 **
  *
  **

The AsciiArt module (old PrettyConsolRepr) purpose a 'new' option of baseline.

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago

Attachment: trac_14266_pretty_console_print-EliX-jbp.patch.gz

new ascii art version

vbraun commented 11 years ago
comment:15

Looks pretty nice! I'm fine with the functionality, but the documentation needs to be improved. Especially since you want to provide other developers a framework to enable ascii art output.

Some of the names are awkward in English. Pretty console representation means that the console is pretty, not the repr(esentation). It would be nice (and it would make writing doctests easier) if we install a global function analogous to repr() to get the ascii art representation. I would suggest we call it display() and the magic %display (instead of %pcp, unless you want to use it as a pun on the hallucinogenic drug).

Similarly, I don't like __pretty_repr__. Its not a variant of __repr__ with pretty output since it returns a specialized object instead of a string. Its also not a Python builtin, so Sage coding style would use only single underscores. How about we call it _ascii_art_()? Similarly, set_display instead of set_pretty_repr.

Also, since this should be a general framework we should add a default _ascii_art_ method (with suitable documentation to benefit developers) to SageObject. For example (in sage_object.pyx):

    def _ascii_art_(self):
        '''
        Return an ASCII art representation.

        To implement multi-line ASCII art output in a derived class
        you must override this method. Unlike :meth:`_repr_`, which is
        sometimes used for the hash key, the output of
        :meth:`_ascii_art_` may depend on settings and is allowed to
        change during runtime. For example,
        :meth:`~sage.combinat.tableau.Tableaux.set_display` can be
        used to switch the ASCII art of tableaux between different
        mathematical conventions.

        OUTPUT:

        An :class:`~sage.misc.ascii_art.AsciiArt` object, see
        :mod:`sage.misc.ascii_art` for details.

        EXAMPLES:

        You can use the :func:`~sage.misc.ascii_art.display` function
        to get the ASCII art representation of any object in Sage::

            sage: display(integral(exp(x^2)/(x+1), x))
            ⌠         
            ⎮  ⎛ 2⎞   
            ⎮  ⎝x ⎠   
            ⎮ ℯ       
            ⎮ ───── dx
            ⎮ x + 1   
            ⌡

        Alternatively, you can use the `%display on/off` magic to
        switch all output to ASCII art and back::

            sage: tab = StandardTableaux(3)[2];  tab
            [[1, 2], [3]]
            sage: %display on
            sage: tab
            1  2
            3
            sage: Tableau.set_display("normal")
            sage: tab
            +---+
            | 3 |
            +---+---+
            | 1 | 2 |
            +---+---+
            sage: %display off

        TESTS::

            sage: 1._ascii_art_()
            1
            sage: type(_)
            sage.misc.ascii_art.AsciiArt
        '''
        from sage.misc.ascii_art import AsciiArt
        return AsciiArt(repr(self))

Finally, __every__ function must be documented and doctested. Please follow http://www.sagemath.org/doc/developer/conventions.html#docstring-markup-with-rest-and-sphinx for the markup. For example, we use an OUTPUT: block instead of @postcondition.

tscrim commented 11 years ago
comment:16

Replying to @vbraun:

Some of the names are awkward in English. Pretty console representation means that the console is pretty, not the repr(esentation). It would be nice (and it would make writing doctests easier) if we install a global function analogous to repr() to get the ascii art representation. I would suggest we call it display() and the magic %display (instead of %pcp, unless you want to use it as a pun on the hallucinogenic drug).

I'm not a big fan of %display, it's somewhat vague as a true/false. How about %ascii_art or have %display accept a string input such as "ascii_art" incase we end up with more display types later on? (I think if you see ascii art, you're trippin' on some %pcp O_o )

Similarly, I don't like __pretty_repr__. Its not a variant of __repr__ with pretty output since it returns a specialized object instead of a string. Its also not a Python builtin, so Sage coding style would use only single underscores. How about we call it _ascii_art_()? Similarly, set_display instead of set_pretty_repr.

I like the _ascii_art_(). However I would rather this be converted into using the GlobalOptions framework instead of a separate function and attribute (tableaux already has this, and I can do it for Dyck paths if preferred). We can also use this for partitions as well.

Also, since this should be a general framework we should add a default _ascii_art_ method (with suitable documentation to benefit developers) to SageObject.

+1

@Jean-Baptiste

I would also like to see AsciiArt inherit from SageObject as well, and subsequently use _repr_ instead of __repr__ (1 underscore instead of 2). Also I would like to see the trailing whitespace removed from the new lines you've added, but this is not too important. (I like your ascii art in the beginning, too bad it's not of a sage [okay, I'm done with bad puns for now, promise].)

Thanks,

Travis

novoselt commented 11 years ago
comment:17

It would be even better if default _ascii_art_ was deTeXed _latex_, rather than just _repr_.

I played with tex2mail a bit and it was quite nice, although I never got to trying to include it into Sage.

vbraun commented 11 years ago
comment:18

Replying to @novoselt:

It would be even better if default _ascii_art_ was deTeXed _latex_, rather than just _repr_. I played with tex2mail a bit and it was quite nice, although I never got to trying to include it into Sage.

There are other implementations of that idea, for example AsciiTeX. But that's not what this ticket is about.

vbraun commented 11 years ago
comment:19

Replying to @tscrim:

I'm not a big fan of %display, it's somewhat vague as a true/false. How about %ascii_art or have %display accept a string input such as "ascii_art" incase we end up with more display types later on?

Sounds good to me. %display ascii_art and %display repr, perhaps with aliases on/off for the lazy.

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:20

Attachment: trac_14266_ascii_art_module-EliX-jbp.patch.gz

New version with several tests for lot of methods... with AsciiArt inherit SageObject. I purpose to use %display ascii_art and display repr to select one use.

I hope I don't forget anything.

Enjoy.

Thanks, Jean-Baptiste

vbraun commented 11 years ago
comment:21

I see you are developing your own whitespace style. Unfortunately, its a slap in Guido Rossum's face. Can you have a look at pep8 ( http://www.python.org/dev/peps/pep-0008/#whitespace-in-expressions-and-statements)?

def display_magic_function( self, mode = 'ascii_art'):  # no
def display_magic_function(self, mode='ascii_art'):     # yes
3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:22

It is unreadable for you or/and you want and I must code like that???

vbraun commented 11 years ago
comment:23

It is not totally unreadable, but reading code that does not follow a consistent styles does require additional mental resources that are better used elsewhere. As annoying as it seems to be on the outset, there have been scientific studies that have found evidence for the benefits of a consistent code style. All large software projects have one, and Sage uses the Python code style.

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:24

Attachment: trac_14266_ascii_art_module-EliX-jbp (1).patch.gz

I hope it's ok now...

vbraun commented 11 years ago
comment:25

What do you mean by

.. TODO::

    revenir dessus..

That method looks fine to me. Is there anything left that you want to change?

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:26

Sorry I forgot to delete this message. It was when I adapted the text to the code... I have nothing to change.

tscrim commented 11 years ago
comment:27

A few more things:

AUTHORS:

- Jean-Baptiste ...
trac_14266: new version of ascii art module and magic function associated.

Thanks.

vbraun commented 11 years ago
comment:28

In fact, do not include the trac ticket number in the commit message. Jeroen's merger script will put the correct number there automatically. Just

New version of ascii art module and associated magic functions
3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:29

Replying to @tscrim:

A few more things:

  • There's still a few functions without doctests (this includes _private_methods).

I added some test... but sometime when the method it just a capsule:

def meth(self):
    return self.other_meth()

or something similar, I just put see :meth:other_meth.

  • Bullet points needs a blankline before them, in particular, the authors block should be
AUTHORS:

- Jean-Baptiste ...

I think it's okay now.

  • Any block should be in all caps, such as EXAMPLES::

I hope it's okay too.

  • I'd prefer using triple double-quotes rather than triple single-quotes for consistancy, but this is not a big issue

I try to do that when it's not my files and I think (but I don't verify) it is consistant in mine.

  • Your commit message should have the description on the first line:
New version of ascii art module and magic function associated.

In the last patch I put that and this one must work on 5.9.

Thanks, Jean-Baptiste

vbraun commented 11 years ago
comment:30

Attachment: trac_14266_ascii_art_module-EliX-jbp.2.patch.gz

I don't like your approach for changing the output, you are monkey-patching sage.misc.displayhook to change its behavior from another module. Thats usually a no-no in production code (unless you cannot control the source that you are patching, which is not the case here). The problem is that if anybody is going to edit sage.misc.displayhook later on, they will inadvertently break your construction since noting in the source code of the displayhook knowns that this is not the actual code that is used when Sage is running. The standard solution to associating state with a function is to use a functionoid, that is, a class with a __call__ method:

class FormatObj():
    def __init__(self):
        self._display = False

    def set_ascii_art(self, ascii_art):
        self._display = bool(ascii_art)

    def __call__(self, obj):
        if self._display:
             ...
        else:
             ....

format_obj = FormatObj()

As a bonus, your code then wouldn't need layers of indirection format_obj -> pretty_format -> simple_format_obj.

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:31

I will try to do that but I'm not really sure to understand the good way...

I have an other problem. I think I don't load display_magic_func.py at the good place and that fail all tests.

vbraun commented 11 years ago
comment:32

The %display magic should be defined in sage.misc.sage_extenison with an @line_magic like the other magics. You shouldn't have to add anything to sage.all_cmdline.

tscrim commented 11 years ago
comment:33

Replying to @sagetrac-elixyre:

Replying to @tscrim:

A few more things:

  • There's still a few functions without doctests (this includes _private_methods).

I added some test... but sometime when the method it just a capsule:

def meth(self):
    return self.other_meth()

or something similar, I just put see :meth:other_meth.

The need doctests not just docstings (there are still non-trivial methods without tests). However if they are simple wrappers like that, just make it an alias:

other_meth = meth
  • I'd prefer using triple double-quotes rather than triple single-quotes for consistency, but this is not a big issue

I try to do that when it's not my files and I think (but I don't verify) it is consistent in mine.

This is not done in other files you've modified (ex. tableau.py), and again, to be consistent with the project as whole, I would recommended using double quotes. However in new files you've created, this is a very minor issue to me (i.e. I won't let it hold up a positive review).

Thanks,

Travis

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:34

Attachment: trac_14266_ascii_art_13_05_14_EliX-jbp.patch.gz

I make some modifications to use correctly displayhook.

I have several problems for make tests: I use the fact to be in a terminal to take the width of the window... excepted in test context it seems to be differents...

File "ascii_art.py", line 25, in sage.misc.ascii_art
Failed example:
    ascii_art(integrate(n^2/(pi*x),x))
Expected:
     2       
    n *log(x)
    ---------
        pi   
Got:
    stty: stdin isn't a terminal
     2       
    n *log(x)
    ---------
        pi 

There is an other points, if we use utf8 in the terminal, it is not utf8 during the tests...

Why don't pass sage to utf8?

Thanks, Jean-Baptiste

vbraun commented 11 years ago
comment:35

The doctesting should be mostly utf-8 clean, I think. See also #14153. What exactly is the problem?

The stty: stdin isn't a terminal comes because the controlling terminal isn't a terminal for doctests. You should set a fixed width (probably 80) independent of the actual terminal size during doctests, something like this:

def terminal_width():
    import os, sys
    from sage.doctest import DOCTEST_MODE
    isatty = os.isatty(sys.stdout.fileno())
    if DOCTEST_MODE or not isatty:
        return 80
    import fcntl, termios, struct
    rc = fcntl.ioctl(int(0), termios.TIOCGWINSZ,
                     struct.pack('HHHH', sys.stdout.fileno(), 0, 0, 0))
    h, w, hp, wp = struct.unpack('HHHH', rc)
    return w

The "pcp" abbreviation is still floating around in a few places, can you replace those?

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago

Attachment: trac_14266_ascii_art_13_05_15_EliX-jbp.patch.gz

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago

Dependencies: #8703

3f8450e1-87bf-41c6-ab53-29a0552debb3 commented 11 years ago
comment:36

Hi,

I'm really bad with mercurial but it seems to be good...

I made that module for see quickly trees in terminal so I added _ascii_art methods for trees. (depends of #8703).

And all "pcp" abbreviation are disappear.

Thanks,

Jean-Baptiste

vbraun commented 11 years ago
comment:37

Looks great! I just have some minor nitpicks:

Apart from that it is good to go.

tscrim commented 11 years ago

Changed dependencies from #8703 to #8703 #14203

tscrim commented 11 years ago

Reviewer: Volker Braun, Travis Scrimshaw

tscrim commented 11 years ago

Description changed:

--- 
+++ 
@@ -15,3 +15,10 @@
    **    **        *    ***     ****
   **             ***

+ +--- + +Apply: + +- attachment: trac_14266_ascii_art_13_05_15_EliX-jbp.patch +- attachment: trac_14266-ascii_art-review-ts.patch

tscrim commented 11 years ago
comment:39

I've uploaded a review patch which makes a variety of changes, including:

Volker, would you be willing review my review patch?

Thanks,

Travis

vbraun commented 11 years ago
comment:40

Patchbot:

Apply trac_14266_ascii_art_13_05_15_EliX-jbp.patch trac_14266-ascii_art-review-ts.patch

vbraun commented 11 years ago
comment:41

I get two test failures on sage-5.10.beta3:

sage -t --long devel/sage/sage/doctest/sources.py  # 1 doctest failed
sage -t --long devel/sage/sage/misc/ascii_art.py  # 1 doctest failed
tscrim commented 11 years ago
comment:42

Fixed:

sage -t --long misc/ascii_art.py
    [116 tests, 4.35 s]
sage -t --long doctest/sources.py
    [327 tests, 122.27 s]
----------------------------------------------------------------------
All tests passed!
----------------------------------------------------------------------

I also removed all trailing whitespace from misc/ascii_art.py and fixed some minor docstring issues.

vbraun commented 11 years ago
comment:43

apply trac_14266_ascii_art_13_05_15_EliX-jbp.patch, trac_14266-ascii_art-review-ts.patch

vbraun commented 11 years ago
comment:44

Travis, your patch only applies with fuzz 2. Can you rebase it against the newest Sage beta?