jeffwright13 / apg_flask

Flask wrapper around the [audio program generator](https://github.com/jeffwright13/audio_program_generator) module
0 stars 3 forks source link

apg 1.8.0.0 (file extension checking) broke apg_flask #27

Closed jeffwright13 closed 2 years ago

jeffwright13 commented 2 years ago
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/flask/app.py", line 2088, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/flask/app.py", line 2073, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/flask/app.py", line 2070, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/flask/app.py", line 1515, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/flask/app.py", line 1513, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/flask/app.py", line 1499, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "/Users/jwr003/coding/apg_flask/app/views.py", line 42, in setvals
    A = apg.AudioProgramGenerator(
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/audio_program_generator/apg.py", line 125, in __init__
    self.filenames_valid = self._validate_filename_extensions(
  File "/Users/jwr003/coding/apg_flask/venv/lib/python3.9/site-packages/audio_program_generator/apg.py", line 215, in _validate_filename_extensions
    return Path(pf.name).suffix == ".txt"
AttributeError: '_io.StringIO' object has no attribute 'name'
jeffwright13 commented 2 years ago

The problem lies in the (incorrect) type annotation def in apg's __init__ function. It claims phrase_file is of type StringIO and sound_file is of type BytesIO; however, what is actually passed to it (and working correctly) in apg's main() are types TextIOWrapper and BufferedReader. Thus, when apg_flask's setvals() method uses StringIO/BytesIO, the desired attribute .name is not found and an exception occurs.

The correct way to fix this is to change lines 40/41 in apg_flask's views.py to conform to the actually-expected types TextIOWrapper and BufferedReader.

jeffwright13 commented 2 years ago

For reference:

>>> from io import StringIO, BytesIO, TextIOWrapper, BufferedReader

>>> dir(StringIO)
['__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'getvalue', 'isatty', 'line_buffering', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
>>>
>>> dir(TextIOWrapper)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
>>>
>>> dir(BytesIO)
['__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', 'close', 'closed', 'detach', 'fileno', 'flush', 'getbuffer', 'getvalue', 'isatty', 'read', 'read1', 'readable', 'readinto', 'readinto1', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
>>>
>>> dir(BufferedReader)
['__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_dealloc_warn', '_finalizing', 'close', 'closed', 'detach', 'fileno', 'flush', 'isatty', 'mode', 'name', 'peek', 'raw', 'read', 'read1', 'readable', 'readinto', 'readinto1', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
jeffwright13 commented 2 years ago

Relevant info from the docs:

The type of file object returned by the open() function depends on the mode. When open() is used to open a file in a text mode ('w', 'r', 'wt', 'rt', etc.), it returns a subclass of io.TextIOBase (specifically io.TextIOWrapper). When used to open a file in a binary mode with buffering, the returned class is a subclass of io.BufferedIOBase. The exact class varies: in read binary mode, it returns an io.BufferedReader; in write binary and append binary modes, it returns an io.BufferedWriter, and in read/write mode, it returns an io.BufferedRandom. When buffering is disabled, the raw stream, a subclass of io.RawIOBase, io.FileIO, is returned.

jeffwright13 commented 2 years ago

^^^ Actually the use of StringIO and BytesIO is probably correct, since they are the in-memory sublasses of the base text and raw/binary classes. I am going to revert the specification of apg 1.8.0.0 here so it is not broken any longer. Will figure this out later.

jeffwright13 commented 2 years ago

Fixed; now referencing apg 1.9.0