Search code examples
pythonstdin

Translate sys.stdin to a variable problem


I have been trying to pass a string from stdin to a python script, but I keep getting an error and I can't figure out why.

Here is the script:

import sys

signal = {'A': '.-',     'B': '-...',   'C': '-.-.',
          'D': '-..',    'E': '.',      'F': '..-.',
          'G': '--.',    'H': '....',   'I': '..',
          'J': '.---',   'K': '-.-',    'L': '.-..',
          'M': '--',     'N': '-.',     'O': '---',
          'P': '.--.',   'Q': '--.-',   'R': '.-.',
          'S': '...',    'T': '-',      'U': '..-',
          'V': '...-',   'W': '.--',    'X': '-..-',
          'Y': '-.--',   'Z': '--..',

          '0': '-----',  '1': '.----',  '2': '..---',
          '3': '...--',  '4': '....-',  '5': '.....',
          '6': '-....',  '7': '--...',  '8': '---..',
          '9': '----.'
         }

plaintext = ''.join(sys.stdin.readlines())

plaintext2 = 'Hello'

def encode(s):
    return ' '.join(signal.get(i.upper()) for i in s)

print('')
print('Results from hardcoded var (plaintext2):')
print('Type:', type(plaintext2))
print('Variable:', plaintext2)
print('Encoded msg:', encode(plaintext2))
print('----------------------------------\n')
print('Results from STDIN var (plaintext):')
print('Type:', type(plaintext))
print('Variable:', plaintext)
print('Encoded msg:', encode(plaintext))

When I run the script I get this:

$ echo Hello | ./morse.py

Results from hardcoded var (plaintext2):
Type: <class 'str'>
Variable: Hello
Encoded msg: .... . .-.. .-.. ---
----------------------------------

Results from STDIN var (plaintext):
Type: <class 'str'>
Variable: Hello

Traceback (most recent call last):
  File "./morse.py", line 56, in <module>
    print('Encoded msg:', encode(plaintext))
  File "./morse.py", line 42, in encode
    return ' '.join(signal.get(i.upper()) for i in s)
TypeError: sequence item 5: expected str instance, NoneType found

My question is why do I get that TypeError when getting the string from the STDIN and it works from a variable in the script.

Edit:

I applied the solutions in the comments from @user2357112 and @Skam.

1 ) Adding some mapping to signal for extra characters.
2 ) Adding the default character '#' to take care of the unmapped ones:

def encode(s):
    return ' '.join(signal.get(i.upper(), '#') for i in s)

Also, thanks @yaccob for your idea about debugging.

def encode(s):
    print([str(c) for c in s])

Solution

  • As others have pointed out, there are characters in your string that your code does not know how to handle. Specifically, signal.get(i.upper()) will return None if i not in signals

    One solution would be to return a default character for all values not found in signals.

    def encode(s):
        return ' '.join(signal.get(i.upper(), '') for i in s)
    

    Another,

    def encode(s):
        return ' '.join(filter(lambda x: x.upper() in signal, s))
    

    I like the latter version because IMO it's a little more clear about what the code is actually doing.