views:

586

answers:

6

I have a bunch of legacy code for encoding raw emails that contains a lot of print statements such as

print >>f, "Content-Type: text/plain"

This is all well and good for emails, but we're now leveraging the same code for outputting HTTP request. The problem is that the Python print statement outputs '\n' whilst HTTP requires '\r\n'.

It looks like Python (2.6.4 at least) generates a trailing PRINT_NEWLINE byte code for a print statement which is implemented as

ceval.c:1582: err = PyFile_WriteString("\n", w);

Thus it appears there's no easy way to override the default newline behaviour of print. I have considered the following solutions

  • After writing the output simply do a .replace('\n', '\r\n'). This will interfere with HTTP messages that use multipart encoding.
  • Create a wrapper around the destination file object and proxy the .write method
  • def write(self, data):
        if data == '\n':
            data = '\r\n'
        return self._file.write(data)
    

  • Write a regular expression that translates print >>f, text to f.write(text + line_end) where line_end can be '\n' or '\r\n'.
  • I believe the third option would be the most appropriate. It would be interesting to hear what your Pythonic approach to the problem would be.

    +6  A: 

    (Not sure how/if this fits with the wrapper you intend to use, but in case...)

    In Python 2.6 (and many preceding versions), you can suppress the newline by adding a comma at the end of the print statement, as in:

    data = 'some msg\r\n'
    print data,  # note the comma
    

    The downside of using this approach however is that the print syntax and behavior is changed in Python3.

    mjv
    A: 

    I think I would define a new function writeline in an inherited file/stream class and update the code to use writeline instead of print. The file object itself can hold the line ending style as a member. That should give you some flexibility in behavior and also make the code a little clearer i.e. f.writeline(text) as opposed to f.write(text+line_end).

    codelogic
    +3  A: 

    In python2.x, I think you can do:

    print >>f "some msg\r\n",
    

    to supress the trailing new line.

    In python3.x, it's a lot simpler:

    print("some msg", end = "\r\n", file = f)
    
    sykora
    A: 

    I also prefer your third solution, but no need to use f.write, any user written function/callable would do. Thus the next changes would become easy. If you use an object you may even hide target file inside it thus removing some syntaxic noise like file or kind of newline.

    Too bad print is a statement in python 2.x, with python 3.x print could simply be overloaded by something user defined.

    kriss
    +6  A: 

    You should solve your problem now and for forever by defining a new output function. Were print a function, this would have been much easier.

    I suggest writing a new output function, mimicing as much of the modern print function signature as possible (because reusing a good interface is good), for example:

    def output(*items, end="\n", file=sys.stdout):
        pass
    

    Once you have replaced all prints in question, you no longer have this problem -- you can always change the behavior of your function instead! This is a big reason why print was made a function in Python 3 -- because in Python 2.x, "all" projects invariably go through the stage where all the print statements are no longer flexible, and there is no easy way out.

    kaizer.se
    +1: Ditch `print` permanently. Think of it as a debugging tool, not a real part of production code.
    S.Lott
    A: 

    Python has modules both to handle email and http headers in an easy compliant way. I suggest you use them instead of solving already solved problems again.

    Lennart Regebro
    +1 Good suggestion, Lennart. We all "humored" the OP in in attempt to "leverage existing code", but depending on his/her situation, you are right, we should always remember about these `"included batteries"`
    mjv
    We are actually using classes derived from those in the stdlib email package. For example email.Generator (Python 2.4) heavily uses print >>. This is all well and good for emails (only need \n), but we're also using the code to parse, modify and regenerate HTTP request (which need \r\n).
    brotchie
    You are using the email module to generate http requests? No wonder you get problems.
    Lennart Regebro