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

Bypass #344

Merged
merged 4 commits into from
Jul 29, 2024
Merged

Bypass #344

merged 4 commits into from
Jul 29, 2024

Conversation

MattCatz
Copy link

@MattCatz MattCatz commented Jul 24, 2024

Description

Bypass buffers with no control chars.

If buffers do not have any control characters then they can be processed in one bulk move rather than byte-by-byte.

This is a lot more efficient with devices that never hit _control_char_sent_limit.

Also the first call to _handle_control_chars in read is unnecessary since _handle_control_chars consumes the entire _raw_buf. (meaning it is always starts empty when you call read)

Notable things not addressed:

  • Most instances of b"" (a bytes structure) should be
    bytearray() since bytes are not mutable.
  • if a control sequence is not contained in a single read it will
    not get processed.
  • The following code would be a little bit more efficient as a
    memoryview since it would skip copying the contents of
    _raw_buf every iteration.
    c, self._raw_buf = self._raw_buf[:1], self._raw_buf[1:]

I left those out to minimize this patch, but can circle back around and fix them later.

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

I have tried the changes out locally with some of the scripts we have around.

Checklist:

  • My code follows the style guidelines of this project (no GitHub actions complaints! run make lint before
    committing!)
  • I have commented my code, pydocstyle and darglint are happy, docstrings are in google docstring format, and all
    docstrings include a summary, args, returns and raises fields (even if N/A)
  • I have added tests that prove my fix is effective or that my feature works, if adding "functional" tests please
    note if there are any considerations for the vrnetlab setup
  • New and existing unit tests pass locally with my changes

Copy link

codecov bot commented Jul 24, 2024

Codecov Report

Attention: Patch coverage is 63.15789% with 7 lines in your changes missing coverage. Please review.

Project coverage is 89.89%. Comparing base (bed9fd3) to head (819c085).
Report is 41 commits behind head on main.

Files Patch % Lines
scrapli/transport/plugins/telnet/transport.py 60.00% 4 Missing ⚠️
scrapli/transport/plugins/asynctelnet/transport.py 66.66% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #344      +/-   ##
==========================================
- Coverage   90.29%   89.89%   -0.41%     
==========================================
  Files          60       60              
  Lines        3533     3551      +18     
==========================================
+ Hits         3190     3192       +2     
- Misses        343      359      +16     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -117,16 +117,25 @@ def _handle_control_chars(self) -> None:
if not self.stdout:
raise ScrapliConnectionNotOpened

if self._raw_buf.find(NULL) != -1:
Copy link
Author

Choose a reason for hiding this comment

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

I added this to preserve the old behavior. If I understand the rest of the code, this isn't needed since _read catches EOFError and should be able to be removed? (not c: would only evaluate to true if c == 0?)

@MattCatz MattCatz marked this pull request as ready for review July 25, 2024 00:18
@carlmontanari
Copy link
Owner

hey again! this looks/feels sane but I prolly gotta look at it closer this weekend or something. all the telnet bits were ported from old std library telnet things in a reasonably faithful fashion so I'm a bit hesitant to change things -- or at least have to look closer since I for sure don't look at these parts too often!

@MattCatz
Copy link
Author

hey again! this looks/feels sane but I prolly gotta look at it closer this weekend or something. all the telnet bits were ported from old std library telnet things in a reasonably faithful fashion so I'm a bit hesitant to change things -- or at least have to look closer since I for sure don't look at these parts too often!

Yeah no rush.

Just looking through the STD library version. The biggest difference that probably slows down this port is the size of the read. The STD library version does fairly small read of 50. While this one seems to grab everything at once. For commands that generate a lot of output, it will make any kind of non-linear inefficiencies are more impactful. Now I'm curious if the overhead I'm seeing would be less with a smaller read lol.

https://github.com/python/cpython/blob/3.12/Lib/telnetlib.py#L527

I also notice that this port has a bug where it does not correctly interpret the escape sequence 0xFF 0xFF as a literal 0xFF.

https://github.com/python/cpython/blob/3.12/Lib/telnetlib.py#L456

Matthew Cather and others added 4 commits July 28, 2024 09:54
If buffers do not have any control characters then they can be
processed in one bulk move rather than byte-by-byte.

This is a lot more efficent with devices that never hit
`_control_char_sent_limit`.

Also the first call to `_handle_control_chars` in `read` is
unnecessary since `_handle_control_chars` consumes the entire
`_raw_buf`. (meaning it is always starts empty when you
call `read`)

Notable things not addressed:
- Most instances of `b""` (a `bytes` structure) should be
  `bytearray()` since `bytes` are not mutable.
- if a control sequence is not contained in a single read it will
  not get processed.
- The following code would be a little bit more efficent as a
  `memoryview` since it would skip copying the contents  of
  `_raw_buf` every iteration.
  `c, self._raw_buf = self._raw_buf[:1], self._raw_buf[1:]`

I left those out to minimize this patch, but can circle back around
and fix them later.
@carlmontanari
Copy link
Owner

phew. fought with ssh2/python packaging a bit this morning then took a look at this! im all python-d out! but this all sounds reasonable to me! I just rebased this off main to make sure the test fixups worked (unrelated to this stuff), and to test things out and seems good. if you're still happy with this we can smack merge!

@MattCatz
Copy link
Author

phew. fought with ssh2/python packaging a bit this morning then took a look at this! im all python-d out! but this all sounds reasonable to me! I just rebased this off main to make sure the test fixups worked (unrelated to this stuff), and to test things out and seems good. if you're still happy with this we can smack merge!

Looks good to me.

@carlmontanari carlmontanari merged commit d594cb0 into carlmontanari:main Jul 29, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants