Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO module enhancements #348

Merged
merged 102 commits into from
Aug 6, 2018
Merged

IO module enhancements #348

merged 102 commits into from
Aug 6, 2018

Conversation

felixdivo
Copy link
Collaborator

@felixdivo felixdivo commented Jul 3, 2018

This PR harmonizes the different CAN message writers and readers, fixes some bugs and adds new features.

Features

  • context manager support for all readers and writers
  • they share a common super class called BaseIOHandler
  • all file handles can now be closed with the stop() method
  • the table name in SqliteReader/SqliteWriter can be adjusted
  • append mode added in CSVWriter and CanutilsLogWriter
  • file-like and path-like objects can now be passed to the readers and writers (except to the Sqlite handlers)
  • add a __ne__() method to the Message class (this was required by the tests)
  • added a stop() method for BufferedReader
  • SqliteWriter: this now guarantees that all messages are being written, exposes some previously internal metrics and only buffers messages up to a certain limit before writing/committing to the database

Breaking changes

  • writing to closed writers is not supported any more (it was supported only for some)
  • the method Listener.on_message_received() is now abstract (using @abc.abstractmethod)
  • the file in the reader/writer is now always stored in the attribute uniformly called file, and not in something like fp, log_file or output_file
  • removed the unused header_line attribute from CSVReader
  • privatized some attributes that are only to be used internally in the classes
  • changed the name of the first parameter of the read/writer constructors from filename to file

Non-breaking changes

  • added documentation
  • removed a lot of noisy debugging statements
  • added quite a few tests

Fixes

  • the __iter__() mthods now call stop() at the end of the iteration
  • turned CSVReader and SqliteReader into new-style classes
  • added CSVReader and SqliteReader to docs
  • code/doc cleanups
  • there are now no leftover files after running the tests
  • SqliteReader.read_all() now returns the messages as can.Message objects and not as raw tuples
  • fix a bug in ASCWriter that created corrupted files

Fixes #268.

@felixdivo
Copy link
Collaborator Author

felixdivo commented Jul 3, 2018

@christiansandberg Do you know what's up with this failing test? It occurs on Python 2.7 only, but on both Linux & Win.

@codecov
Copy link

codecov bot commented Jul 3, 2018

Codecov Report

Merging #348 into develop will increase coverage by 0.45%.
The diff coverage is 96.03%.

@@            Coverage Diff             @@
##           develop    #348      +/-   ##
==========================================
+ Coverage    58.85%   59.3%   +0.45%     
==========================================
  Files           54      55       +1     
  Lines         4214    4241      +27     
==========================================
+ Hits          2480    2515      +35     
+ Misses        1734    1726       -8

@christiansandberg
Copy link
Collaborator

Why have you changed to use universal newlines which is deprecated and especially on binary files?

Copy link
Collaborator

@christiansandberg christiansandberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try not to change things if you don't know what you are changing. ;)

"""
super(BaseIOHandler, self).__init__() # for multiple inheritance
if open_file:
self.file = open(filename, mode)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if it could accept "filename" being an already open file object as well. That could open up possibilities to use other streams like over the network or compressing files on the fly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I thought about that as well. I might add it here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

can/io/csv.py Outdated
is_error_frame=(error == '1'),
is_remote_frame=bool(remote),
extended_id=bool(extended),
is_error_frame=bool(error),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All non-empty strings are True.

@felixdivo
Copy link
Collaborator Author

I did not know that setting the universal newlines parameter with U was deprecated.

elif filename.endswith(".db"):
return SqliteWriter(filename, *args, **kwargs)
elif filename.endswith(".log"):
return CanutilsLogWriter(filename, *args, **kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be an else here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, actually not, since if none of the file types matches, it should fall back to using the Printer.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sorry. I missed that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem.

can/listener.py Outdated
if the reader has already been stopped
"""
if self.is_stopped:
raise BufferError("reader has already been stopped")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure BufferError is appropriate here. Maybe just RuntimeError or something.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exception BufferError: Raised when a buffer related operation cannot be performed.

It sort of fits, but RuntimeError does as well. I'll use RuntimeError.

can/message.py Outdated
self.bitrate_switch == other.bitrate_switch
)
else:
raise NotImplementedError()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t it be just False?

Copy link
Collaborator Author

@felixdivo felixdivo Jul 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A rich comparison method may return the singleton NotImplemented if it does not implement the operation for a given pair of arguments.

Do we explicitly not support it or is it just False? Maybe, the other object would be able to compare a message to them?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still, we should return the singleton and not raise the Error if we decide to leave it like this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to this, when we return NotImplemented, the other object "b" is asked if it can compare itself to the message. I guess that's desirable, or at least not harming.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

@felixdivo
Copy link
Collaborator Author

I want to wait for #380 before merging.

@felixdivo
Copy link
Collaborator Author

@christiansandberg what about #348 (comment) ? Do you have an opinion?

@felixdivo
Copy link
Collaborator Author

Waiting for appveyor/ci#2547 to solve the permissions problem ...

@christiansandberg
Copy link
Collaborator

I added that functionality to blf and asc formats as you can log free text events apart from CAN messages, and since you shouldn’t have entries with a timestamp from 1970 it made sense to pick something valid. This format can only log CAN messages and they could be assumed to always have a timestamp so in this case I don’t think it matters.

@felixdivo
Copy link
Collaborator Author

Ah I see, that feature was only meant for free text events. I'll adjust the tests accordingly to allow this type of behavior.

@felixdivo
Copy link
Collaborator Author

Apart from the permissions problem mentioned above and the problem in #380, this now seems to work. We should wait for both to be solved/merged before merging this PR into develop, just to be sure to not introduce any bugs into the develop branch with this huge PR.

@felixdivo
Copy link
Collaborator Author

Waiting for appveyor/ci#2547 to solve the permissions problem ...

This is now solved.

@felixdivo felixdivo merged commit b749572 into develop Aug 6, 2018
@felixdivo felixdivo deleted the fix-file-io branch August 6, 2018 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

File handles in can.io.* are not always released
2 participants