Merge ~jugmac00/lazr.sshserver:apply-black into lazr.sshserver:main

Proposed by Jürgen Gmach
Status: Merged
Merged at revision: 316d2b283f1fb1238d36d7d7d225b7f89b10e6a4
Proposed branch: ~jugmac00/lazr.sshserver:apply-black
Merge into: lazr.sshserver:main
Diff against target: 1965 lines (+534/-410)
18 files modified
.git-blame-ignore-revs (+2/-0)
.pre-commit-config.yaml (+15/-2)
NEWS.txt (+2/-0)
pyproject.toml (+8/-0)
setup.py (+34/-34)
src/lazr/__init__.py (+2/-0)
src/lazr/sshserver/accesslog.py (+5/-8)
src/lazr/sshserver/auth.py (+75/-64)
src/lazr/sshserver/events.py (+26/-30)
src/lazr/sshserver/service.py (+39/-28)
src/lazr/sshserver/session.py (+12/-16)
src/lazr/sshserver/sftp.py (+5/-4)
src/lazr/sshserver/tests/test_accesslog.py (+20/-20)
src/lazr/sshserver/tests/test_auth.py (+216/-154)
src/lazr/sshserver/tests/test_docs.py (+23/-14)
src/lazr/sshserver/tests/test_events.py (+14/-15)
src/lazr/sshserver/tests/test_session.py (+35/-20)
tox.ini (+1/-1)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+411678@code.launchpad.net

Commit message

Apply black code formatter

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
2new file mode 100644
3index 0000000..3a635e3
4--- /dev/null
5+++ b/.git-blame-ignore-revs
6@@ -0,0 +1,2 @@
7+# apply black
8+a08ff20b65010305eaf0a4de60c7913146658a76
9diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
10index d62393a..d4436ec 100644
11--- a/.pre-commit-config.yaml
12+++ b/.pre-commit-config.yaml
13@@ -2,14 +2,27 @@
14 # See https://pre-commit.com/hooks.html for more hooks
15 repos:
16 - repo: https://github.com/pre-commit/pre-commit-hooks
17- rev: v3.2.0
18+ rev: v4.0.1
19 hooks:
20 - id: check-added-large-files
21+ - id: check-ast
22+ - id: check-json
23 - id: check-merge-conflict
24+ - id: check-toml
25 - id: check-xml
26 - id: check-yaml
27 - id: debug-statements
28+ - id: end-of-file-fixer
29+ - id: trailing-whitespace
30+- repo: https://github.com/psf/black
31+ rev: 21.10b0
32+ hooks:
33+ - id: black
34 - repo: https://github.com/PyCQA/flake8
35- rev: 3.9.2
36+ rev: 4.0.1
37 hooks:
38 - id: flake8
39+- repo: https://github.com/PyCQA/isort
40+ rev: 5.10.1
41+ hooks:
42+ - id: isort
43diff --git a/NEWS.txt b/NEWS.txt
44index 2981bee..43c2138 100644
45--- a/NEWS.txt
46+++ b/NEWS.txt
47@@ -7,6 +7,8 @@ NEWS for lazr.sshserver
48
49 - Officially add support for Python 3.9 and 3.10.
50 - Add basic pre-commit configuration.
51+- Apply black code formatter.
52+- Add isort pre-commit hook.
53
54 0.1.12 (2021-09-13)
55 ===================
56diff --git a/pyproject.toml b/pyproject.toml
57new file mode 100644
58index 0000000..15e7219
59--- /dev/null
60+++ b/pyproject.toml
61@@ -0,0 +1,8 @@
62+[tool.black]
63+line-length = 79
64+target-version = ['py27']
65+
66+[tool.isort]
67+profile = "black"
68+multi_line_output = 3
69+line_length = 79
70diff --git a/setup.py b/setup.py
71index 2d54c2e..ce0f879 100755
72--- a/setup.py
73+++ b/setup.py
74@@ -16,52 +16,52 @@
75 # You should have received a copy of the GNU Lesser General Public License
76 # along with lazr.sshserver. If not, see <http://www.gnu.org/licenses/>.
77
78-from setuptools import setup, find_packages
79+from setuptools import find_packages, setup
80
81
82 # generic helpers primarily for the long_description
83 def generate(*docname_or_string):
84 res = []
85 for value in docname_or_string:
86- if value.endswith('.txt'):
87+ if value.endswith(".txt"):
88 f = open(value)
89 value = f.read()
90 f.close()
91 res.append(value)
92- if not value.endswith('\n'):
93- res.append('')
94- return '\n'.join(res)
95+ if not value.endswith("\n"):
96+ res.append("")
97+ return "\n".join(res)
98+
99+
100 # end generic helpers
101
102
103 setup(
104- name='lazr.sshserver',
105- version='0.1.12',
106- namespace_packages=['lazr'],
107- packages=find_packages('src'),
108- package_dir={'': 'src'},
109+ name="lazr.sshserver",
110+ version="0.1.12",
111+ namespace_packages=["lazr"],
112+ packages=find_packages("src"),
113+ package_dir={"": "src"},
114 include_package_data=True,
115 zip_safe=False,
116- maintainer='LAZR Developers',
117- maintainer_email='lazr-developers@lists.launchpad.net',
118- description=open('README.txt').readline().strip(),
119- long_description=generate(
120- 'src/lazr/sshserver/README.txt',
121- 'NEWS.txt'),
122- license='LGPL v3',
123+ maintainer="LAZR Developers",
124+ maintainer_email="lazr-developers@lists.launchpad.net",
125+ description=open("README.txt").readline().strip(),
126+ long_description=generate("src/lazr/sshserver/README.txt", "NEWS.txt"),
127+ license="LGPL v3",
128 install_requires=[
129- 'setuptools',
130- 'Twisted[conch]>=16.2.0',
131- 'zope.component',
132- 'zope.event',
133- 'zope.interface',
134- ],
135- url='https://launchpad.net/lazr.sshserver',
136+ "setuptools",
137+ "Twisted[conch]>=16.2.0",
138+ "zope.component",
139+ "zope.event",
140+ "zope.interface",
141+ ],
142+ url="https://launchpad.net/lazr.sshserver",
143 project_urls={
144 "Source": "https://code.launchpad.net/lazr.sshserver",
145 "Issue Tracker": "https://bugs.launchpad.net/lazr.sshserver",
146 },
147- download_url='https://launchpad.net/lazr.sshserver/+download',
148+ download_url="https://launchpad.net/lazr.sshserver/+download",
149 classifiers=[
150 "Development Status :: 5 - Production/Stable",
151 "Intended Audience :: Developers",
152@@ -77,16 +77,16 @@ setup(
153 "Programming Language :: Python :: 3.8",
154 "Programming Language :: Python :: 3.9",
155 "Programming Language :: Python :: 3.10",
156- ],
157+ ],
158 extras_require=dict(
159- docs=['Sphinx'],
160+ docs=["Sphinx"],
161 test=[
162 'bzr; python_version < "3.0"',
163- 'fixtures',
164- 'flake8',
165- 'testtools',
166- 'zope.testrunner',
167- ],
168+ "fixtures",
169+ "flake8",
170+ "testtools",
171+ "zope.testrunner",
172+ ],
173 ),
174- test_suite='lazr.sshserver.tests',
175- )
176+ test_suite="lazr.sshserver.tests",
177+)
178diff --git a/src/lazr/__init__.py b/src/lazr/__init__.py
179index ee457d8..1b88851 100644
180--- a/src/lazr/__init__.py
181+++ b/src/lazr/__init__.py
182@@ -17,7 +17,9 @@
183 # this is a namespace package
184 try:
185 import pkg_resources
186+
187 pkg_resources.declare_namespace(__name__)
188 except ImportError:
189 import pkgutil
190+
191 __path__ = pkgutil.extend_path(__path__, __name__)
192diff --git a/src/lazr/sshserver/accesslog.py b/src/lazr/sshserver/accesslog.py
193index 7471d7d..c18c7a9 100644
194--- a/src/lazr/sshserver/accesslog.py
195+++ b/src/lazr/sshserver/accesslog.py
196@@ -7,19 +7,15 @@ from __future__ import absolute_import, print_function
197
198 __metaclass__ = type
199 __all__ = [
200- 'LoggingManager',
201- ]
202+ "LoggingManager",
203+]
204
205 import logging
206 from logging.handlers import WatchedFileHandler
207
208-from zope.component import (
209- adapter,
210- getGlobalSiteManager,
211- provideHandler,
212- )
213 # This non-standard import is necessary to hook up the event system.
214 import zope.component.event # noqa: F401
215+from zope.component import adapter, getGlobalSiteManager, provideHandler
216
217 from lazr.sshserver.events import ILoggingEvent
218
219@@ -55,7 +51,8 @@ class LoggingManager:
220 log.addHandler(self._main_handler)
221 self._access_handler = WatchedFileHandler(self._access_log_path)
222 self._access_handler.setFormatter(
223- logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
224+ logging.Formatter("%(asctime)s %(levelname)s %(message)s")
225+ )
226 self._access_log.addHandler(self._access_handler)
227 self._access_log.setLevel(logging.INFO)
228 # Make sure that our logging event handler is there, ready to receive
229diff --git a/src/lazr/sshserver/auth.py b/src/lazr/sshserver/auth.py
230index f5510e9..3d88930 100644
231--- a/src/lazr/sshserver/auth.py
232+++ b/src/lazr/sshserver/auth.py
233@@ -13,29 +13,20 @@ from __future__ import absolute_import, print_function
234
235 __metaclass__ = type
236 __all__ = [
237- 'LaunchpadAvatar',
238- 'PublicKeyFromLaunchpadChecker',
239- 'SSHUserAuthServer',
240- ]
241+ "LaunchpadAvatar",
242+ "PublicKeyFromLaunchpadChecker",
243+ "SSHUserAuthServer",
244+]
245
246 import base64
247 import binascii
248 import sys
249
250 from twisted.conch import avatar
251-from twisted.conch.error import (
252- ConchError,
253- ValidPublicKey,
254- )
255+from twisted.conch.error import ConchError, ValidPublicKey
256 from twisted.conch.interfaces import IConchUser
257-from twisted.conch.ssh import (
258- keys,
259- userauth,
260- )
261-from twisted.conch.ssh.common import (
262- getNS,
263- NS,
264- )
265+from twisted.conch.ssh import keys, userauth
266+from twisted.conch.ssh.common import NS, getNS
267 from twisted.cred import credentials
268 from twisted.cred.checkers import ICredentialsChecker
269 from twisted.cred.error import UnauthorizedLogin
270@@ -50,7 +41,6 @@ from lazr.sshserver import events
271 from lazr.sshserver.session import PatchedSSHSession
272 from lazr.sshserver.sftp import FileTransferServer
273
274-
275 log = Logger()
276
277
278@@ -77,11 +67,12 @@ class NoSuchPersonWithName(xmlrpc.Fault):
279 """There's no Person with the specified name registered in Launchpad."""
280
281 error_code = 200
282- msg_template = 'No such person or team: %(person_name)s'
283+ msg_template = "No such person or team: %(person_name)s"
284
285 def __init__(self, person_name):
286 super(NoSuchPersonWithName, self).__init__(
287- self.error_code, self.msg_template % {"person_name": person_name})
288+ self.error_code, self.msg_template % {"person_name": person_name}
289+ )
290
291
292 class LaunchpadAvatar(avatar.ConchUser):
293@@ -100,14 +91,14 @@ class LaunchpadAvatar(avatar.ConchUser):
294 `IAuthServer.getUserAndSSHKeys`.
295 """
296 avatar.ConchUser.__init__(self)
297- self.user_id = user_dict['id']
298- self.username = user_dict['name']
299+ self.user_id = user_dict["id"]
300+ self.username = user_dict["name"]
301
302 # Set the only channel as a standard SSH session (with a couple of bug
303 # fixes).
304- self.channelLookup = {b'session': PatchedSSHSession}
305+ self.channelLookup = {b"session": PatchedSSHSession}
306 # ...and set the only subsystem to be SFTP.
307- self.subsystemLookup = {b'sftp': FileTransferServer}
308+ self.subsystemLookup = {b"sftp": FileTransferServer}
309
310 def logout(self):
311 notify(events.UserLoggedOut(self))
312@@ -127,7 +118,8 @@ class SSHPrivateKeyWithMind(credentials.SSHPrivateKey):
313
314 def __init__(self, username, algName, blob, sigData, signature, mind):
315 credentials.SSHPrivateKey.__init__(
316- self, username, algName, blob, sigData, signature)
317+ self, username, algName, blob, sigData, signature
318+ )
319 self.mind = mind
320
321
322@@ -154,11 +146,11 @@ class UserDetailsMind:
323 endpoint.
324 :param username: The username to look up.
325 """
326- username = username.decode('UTF-8')
327+ username = username.decode("UTF-8")
328 if username in self.cache:
329 return defer.succeed(self.cache[username])
330 else:
331- d = proxy.callRemote('getUserAndSSHKeys', username)
332+ d = proxy.callRemote("getUserAndSSHKeys", username)
333 d.addBoth(self._add_to_cache, username)
334 return d
335
336@@ -189,12 +181,13 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer):
337 self._configured_banner_sent = False
338 self._mind = UserDetailsMind()
339 self.interfaceToMethod = userauth.SSHUserAuthServer.interfaceToMethod
340- self.interfaceToMethod[ISSHPrivateKeyWithMind] = b'publickey'
341+ self.interfaceToMethod[ISSHPrivateKeyWithMind] = b"publickey"
342
343- def sendBanner(self, text, language='en'):
344- bytes = b'\r\n'.join(text.encode('UTF8').splitlines() + [b''])
345- self.transport.sendPacket(userauth.MSG_USERAUTH_BANNER,
346- NS(bytes) + NS(language))
347+ def sendBanner(self, text, language="en"):
348+ bytes = b"\r\n".join(text.encode("UTF8").splitlines() + [b""])
349+ self.transport.sendPacket(
350+ userauth.MSG_USERAUTH_BANNER, NS(bytes) + NS(language)
351+ )
352
353 def _sendConfiguredBanner(self, passed_through):
354 if not self._configured_banner_sent and self._banner:
355@@ -214,7 +207,7 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer):
356 self.method = method
357 d = self.tryAuth(method, user, rest)
358 if not d:
359- self._ebBadAuth(failure.Failure(ConchError('auth returned none')))
360+ self._ebBadAuth(failure.Failure(ConchError("auth returned none")))
361 return
362 d.addCallback(self._sendConfiguredBanner)
363 d.addCallback(self._cbFinishedAuth)
364@@ -246,8 +239,9 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer):
365 """
366 return self._mind
367
368- def makePublicKeyCredentials(self, username, algName, blob, sigData,
369- signature):
370+ def makePublicKeyCredentials(
371+ self, username, algName, blob, sigData, signature
372+ ):
373 """Construct credentials for a request to login with a public key.
374
375 Our implementation returns a SSHPrivateKeyWithMind.
376@@ -261,7 +255,8 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer):
377 """
378 mind = self.getMind()
379 return SSHPrivateKeyWithMind(
380- username, algName, blob, sigData, signature, mind)
381+ username, algName, blob, sigData, signature, mind
382+ )
383
384 def auth_publickey(self, packet):
385 # This is copied and pasted from twisted/conch/ssh/userauth.py in
386@@ -278,30 +273,38 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer):
387 # Work around a bug in paramiko < 2.0.0: if the most significant
388 # byte of an RSA signature is zero, then it strips leading zero
389 # bytes rather than zero-padding it to the correct length.
390- if algName == b'ssh-rsa':
391+ if algName == b"ssh-rsa":
392 signatureType, rawSignature, rest = getNS(signature, 2)
393 pubKeyLen = (pubKey.size() + 7) // 8
394 if len(rawSignature) < pubKeyLen:
395 rawSignature = (
396- b'\x00' * (pubKeyLen - len(rawSignature)) +
397- rawSignature)
398+ b"\x00" * (pubKeyLen - len(rawSignature))
399+ + rawSignature
400+ )
401 signature = NS(signatureType) + NS(rawSignature) + rest
402 b = (
403- NS(self.transport.sessionID) +
404- _bytesChr(userauth.MSG_USERAUTH_REQUEST) + NS(self.user) +
405- NS(self.nextService) + NS(b'publickey') +
406- _bytesChr(hasSig) + NS(pubKey.sshType()) + NS(blob))
407+ NS(self.transport.sessionID)
408+ + _bytesChr(userauth.MSG_USERAUTH_REQUEST)
409+ + NS(self.user)
410+ + NS(self.nextService)
411+ + NS(b"publickey")
412+ + _bytesChr(hasSig)
413+ + NS(pubKey.sshType())
414+ + NS(blob)
415+ )
416 # The next three lines are different from the original.
417 c = self.makePublicKeyCredentials(
418- self.user, algName, blob, b, signature)
419+ self.user, algName, blob, b, signature
420+ )
421 return self.portal.login(c, self.getMind(), IConchUser)
422 else:
423 # The next four lines are different from the original.
424 c = self.makePublicKeyCredentials(
425- self.user, algName, blob, None, None)
426- return self.portal.login(
427- c, self.getMind(), IConchUser).addErrback(
428- self._ebCheckKey, packet[1:])
429+ self.user, algName, blob, None, None
430+ )
431+ return self.portal.login(c, self.getMind(), IConchUser).addErrback(
432+ self._ebCheckKey, packet[1:]
433+ )
434
435
436 # XXX cjwatson 2019-10-18: Ideally we'd use
437@@ -315,6 +318,7 @@ class PublicKeyFromLaunchpadChecker:
438
439 It knows how to get the public keys from the authserver.
440 """
441+
442 credentialInterfaces = (ISSHPrivateKeyWithMind,)
443
444 def __init__(self, authserver):
445@@ -333,13 +337,17 @@ class PublicKeyFromLaunchpadChecker:
446 has registered in Launchpad.
447 """
448 try:
449- username = credentials.username.decode('UTF-8')
450+ username = credentials.username.decode("UTF-8")
451 except UnicodeDecodeError:
452 # Launchpad account names must be valid UTF-8.
453- return defer.fail(UserDisplayedUnauthorizedLogin(
454- "No such Launchpad account: %r" % credentials.username))
455+ return defer.fail(
456+ UserDisplayedUnauthorizedLogin(
457+ "No such Launchpad account: %r" % credentials.username
458+ )
459+ )
460 d = credentials.mind.lookupUserDetails(
461- self.authserver, credentials.username)
462+ self.authserver, credentials.username
463+ )
464 d.addCallback(self._checkForAuthorizedKey, credentials)
465 d.addErrback(self._reportNoSuchUser, username)
466 return d
467@@ -350,29 +358,31 @@ class PublicKeyFromLaunchpadChecker:
468 fault = failure.value
469 if fault.faultCode == NoSuchPersonWithName.error_code:
470 raise UserDisplayedUnauthorizedLogin(
471- "No such Launchpad account: %s" % username)
472+ "No such Launchpad account: %s" % username
473+ )
474 raise failure
475
476 def _checkForAuthorizedKey(self, user_dict, credentials):
477 """Check the key data in credentials against the keys found in LP."""
478- if credentials.algName == b'ssh-dss':
479- wantKeyType = 'DSA'
480- elif credentials.algName == b'ssh-rsa':
481- wantKeyType = 'RSA'
482- elif credentials.algName.startswith(b'ecdsa-sha2-'):
483- wantKeyType = 'ECDSA'
484- elif credentials.algName == b'ssh-ed25519':
485- wantKeyType = 'ED25519'
486+ if credentials.algName == b"ssh-dss":
487+ wantKeyType = "DSA"
488+ elif credentials.algName == b"ssh-rsa":
489+ wantKeyType = "RSA"
490+ elif credentials.algName.startswith(b"ecdsa-sha2-"):
491+ wantKeyType = "ECDSA"
492+ elif credentials.algName == b"ssh-ed25519":
493+ wantKeyType = "ED25519"
494 else:
495 # unknown key type
496 return False
497
498- if len(user_dict['keys']) == 0:
499+ if len(user_dict["keys"]) == 0:
500 raise UserDisplayedUnauthorizedLogin(
501 "Launchpad user '%s' doesn't have a registered SSH key"
502- % user_dict['name'])
503+ % user_dict["name"]
504+ )
505
506- for keytype, keytext in user_dict['keys']:
507+ for keytype, keytext in user_dict["keys"]:
508 if keytype != wantKeyType:
509 continue
510 try:
511@@ -383,7 +393,8 @@ class PublicKeyFromLaunchpadChecker:
512
513 raise UnauthorizedLogin(
514 "Your SSH key does not match any key registered for Launchpad "
515- "user %s" % user_dict['name'])
516+ "user %s" % user_dict["name"]
517+ )
518
519 def _verifyKey(self, validKey, credentials):
520 """Check whether the credentials themselves are valid.
521diff --git a/src/lazr/sshserver/events.py b/src/lazr/sshserver/events.py
522index ccc59be..5d0165b 100644
523--- a/src/lazr/sshserver/events.py
524+++ b/src/lazr/sshserver/events.py
525@@ -7,27 +7,23 @@ from __future__ import absolute_import, print_function
526
527 __metaclass__ = type
528 __all__ = [
529- 'AuthenticationFailed',
530- 'AvatarEvent',
531- 'ILoggingEvent',
532- 'LoggingEvent',
533- 'ServerStarting',
534- 'ServerStopped',
535- 'SFTPClosed',
536- 'SFTPStarted',
537- 'UserConnected',
538- 'UserDisconnected',
539- 'UserLoggedIn',
540- 'UserLoggedOut',
541- ]
542+ "AuthenticationFailed",
543+ "AvatarEvent",
544+ "ILoggingEvent",
545+ "LoggingEvent",
546+ "ServerStarting",
547+ "ServerStopped",
548+ "SFTPClosed",
549+ "SFTPStarted",
550+ "UserConnected",
551+ "UserDisconnected",
552+ "UserLoggedIn",
553+ "UserLoggedOut",
554+]
555
556 import logging
557
558-from zope.interface import (
559- Attribute,
560- implementer,
561- Interface,
562- )
563+from zope.interface import Attribute, Interface, implementer
564
565
566 class ILoggingEvent(Interface):
567@@ -81,29 +77,28 @@ class LoggingEvent:
568 class ServerStarting(LoggingEvent):
569
570 level = logging.INFO
571- template = '---- Server started ----'
572+ template = "---- Server started ----"
573
574
575 class ServerStopped(LoggingEvent):
576
577 level = logging.INFO
578- template = '---- Server stopped ----'
579+ template = "---- Server stopped ----"
580
581
582 class UserConnected(LoggingEvent):
583
584 level = logging.INFO
585- template = '[%(session_id)s] %(address)s connected.'
586+ template = "[%(session_id)s] %(address)s connected."
587
588 def __init__(self, transport, address):
589- LoggingEvent.__init__(
590- self, session_id=id(transport), address=address)
591+ LoggingEvent.__init__(self, session_id=id(transport), address=address)
592
593
594 class AuthenticationFailed(LoggingEvent):
595
596 level = logging.INFO
597- template = '[%(session_id)s] failed to authenticate.'
598+ template = "[%(session_id)s] failed to authenticate."
599
600 def __init__(self, transport):
601 LoggingEvent.__init__(self, session_id=id(transport))
602@@ -112,7 +107,7 @@ class AuthenticationFailed(LoggingEvent):
603 class UserDisconnected(LoggingEvent):
604
605 level = logging.INFO
606- template = '[%(session_id)s] disconnected.'
607+ template = "[%(session_id)s] disconnected."
608
609 def __init__(self, transport):
610 LoggingEvent.__init__(self, session_id=id(transport))
611@@ -126,24 +121,25 @@ class AvatarEvent(LoggingEvent):
612 def __init__(self, avatar):
613 self.avatar = avatar
614 LoggingEvent.__init__(
615- self, session_id=id(avatar.transport), username=avatar.username)
616+ self, session_id=id(avatar.transport), username=avatar.username
617+ )
618
619
620 class UserLoggedIn(AvatarEvent):
621
622- template = '[%(session_id)s] %(username)s logged in.'
623+ template = "[%(session_id)s] %(username)s logged in."
624
625
626 class UserLoggedOut(AvatarEvent):
627
628- template = '[%(session_id)s] %(username)s disconnected.'
629+ template = "[%(session_id)s] %(username)s disconnected."
630
631
632 class SFTPStarted(AvatarEvent):
633
634- template = '[%(session_id)s] %(username)s started SFTP session.'
635+ template = "[%(session_id)s] %(username)s started SFTP session."
636
637
638 class SFTPClosed(AvatarEvent):
639
640- template = '[%(session_id)s] %(username)s closed SFTP session.'
641+ template = "[%(session_id)s] %(username)s closed SFTP session."
642diff --git a/src/lazr/sshserver/service.py b/src/lazr/sshserver/service.py
643index 023d866..2d0622f 100644
644--- a/src/lazr/sshserver/service.py
645+++ b/src/lazr/sshserver/service.py
646@@ -10,17 +10,14 @@ from __future__ import absolute_import, print_function
647
648 __metaclass__ = type
649 __all__ = [
650- 'SSHService',
651- ]
652+ "SSHService",
653+]
654
655
656 import logging
657 import os
658
659-from twisted.application import (
660- service,
661- strports,
662- )
663+from twisted.application import service, strports
664 from twisted.conch.openssh_compat import primes
665 from twisted.conch.ssh.factory import SSHFactory
666 from twisted.conch.ssh.keys import Key
667@@ -28,10 +25,7 @@ from twisted.conch.ssh.transport import SSHServerTransport
668 from twisted.internet import defer
669 from zope.event import notify
670
671-from lazr.sshserver import (
672- accesslog,
673- events,
674- )
675+from lazr.sshserver import accesslog, events
676 from lazr.sshserver.auth import SSHUserAuthServer
677
678
679@@ -48,6 +42,7 @@ def gatherResults(deferredList):
680 :type deferredList: list of `defer.Deferred`s.
681 :return: `defer.Deferred`.
682 """
683+
684 def convert_first_error_to_real(failure):
685 failure.trap(defer.FirstError)
686 return failure.value.subFailure
687@@ -59,7 +54,6 @@ def gatherResults(deferredList):
688
689
690 class KeepAliveSettingSSHServerTransport(SSHServerTransport):
691-
692 def connectionMade(self):
693 SSHServerTransport.connectionMade(self)
694 self.transport.setTcpKeepAlive(True)
695@@ -75,8 +69,9 @@ class Factory(SSHFactory):
696
697 protocol = KeepAliveSettingSSHServerTransport
698
699- def __init__(self, portal, private_key, public_key, banner=None,
700- moduli_path=None):
701+ def __init__(
702+ self, portal, private_key, public_key, banner=None, moduli_path=None
703+ ):
704 """Construct an SSH factory.
705
706 :param portal: The portal used to turn credentials into users.
707@@ -92,14 +87,14 @@ class Factory(SSHFactory):
708 # at it. (Look for the beautiful line "self.portal =
709 # self.transport.factory.portal").
710 self.portal = portal
711- self.services[b'ssh-userauth'] = self._makeAuthServer
712+ self.services[b"ssh-userauth"] = self._makeAuthServer
713 self._private_key = private_key
714 self._public_key = public_key
715 self._banner = banner
716 self._moduli_path = moduli_path
717
718 def _makeAuthServer(self, *args, **kwargs):
719- kwargs['banner'] = self._banner
720+ kwargs["banner"] = self._banner
721 return SSHUserAuthServer(*args, **kwargs)
722
723 def buildProtocol(self, address):
724@@ -110,8 +105,9 @@ class Factory(SSHFactory):
725 """
726 transport = SSHFactory.buildProtocol(self, address)
727 transport._realConnectionLost = transport.connectionLost
728- transport.connectionLost = (
729- lambda reason: self.connectionLost(transport, reason))
730+ transport.connectionLost = lambda reason: self.connectionLost(
731+ transport, reason
732+ )
733 notify(events.UserConnected(transport, address))
734 return transport
735
736@@ -130,7 +126,7 @@ class Factory(SSHFactory):
737 #
738 # b) the server doesn't normally generate a "go away" event.
739 # Rather, the client simply stops trying.
740- if getattr(transport, 'avatar', None) is None:
741+ if getattr(transport, "avatar", None) is None:
742 notify(events.AuthenticationFailed(transport))
743 notify(events.UserDisconnected(transport))
744
745@@ -139,14 +135,14 @@ class Factory(SSHFactory):
746
747 See `SSHFactory.getPublicKeys`.
748 """
749- return {b'ssh-rsa': self._public_key}
750+ return {b"ssh-rsa": self._public_key}
751
752 def getPrivateKeys(self):
753 """Return the server's configured private key.
754
755 See `SSHFactory.getPrivateKeys`.
756 """
757- return {b'ssh-rsa': self._private_key}
758+ return {b"ssh-rsa": self._private_key}
759
760 def getPrimes(self):
761 try:
762@@ -160,9 +156,19 @@ class Factory(SSHFactory):
763 class SSHService(service.Service):
764 """A Twisted service for the SSH server."""
765
766- def __init__(self, portal, private_key_path, public_key_path,
767- main_log, access_log, access_log_path, strport='tcp:22',
768- factory_decorator=None, banner=None, moduli_path=None):
769+ def __init__(
770+ self,
771+ portal,
772+ private_key_path,
773+ public_key_path,
774+ main_log,
775+ access_log,
776+ access_log_path,
777+ strport="tcp:22",
778+ factory_decorator=None,
779+ banner=None,
780+ moduli_path=None,
781+ ):
782 """Construct an SSH service.
783
784 :param portal: The `twisted.cred.portal.Portal` that turns
785@@ -189,7 +195,8 @@ class SSHService(service.Service):
786 private_key=Key.fromFile(private_key_path),
787 public_key=Key.fromFile(public_key_path),
788 banner=banner,
789- moduli_path=moduli_path)
790+ moduli_path=moduli_path,
791+ )
792 if factory_decorator is not None:
793 ssh_factory = factory_decorator(ssh_factory)
794 self.service = strports.service(strport, ssh_factory)
795@@ -202,7 +209,8 @@ class SSHService(service.Service):
796 self.manager = accesslog.LoggingManager(
797 logging.getLogger(self._main_log),
798 logging.getLogger(self._access_log_path),
799- self._access_log_path)
800+ self._access_log_path,
801+ )
802 self.manager.setUp()
803 notify(events.ServerStarting())
804 # By default, only the owner of files should be able to write to them.
805@@ -214,9 +222,12 @@ class SSHService(service.Service):
806
807 def stopService(self):
808 """Stop the SSH service."""
809- deferred = gatherResults([
810- defer.maybeDeferred(service.Service.stopService, self),
811- defer.maybeDeferred(self.service.stopService)])
812+ deferred = gatherResults(
813+ [
814+ defer.maybeDeferred(service.Service.stopService, self),
815+ defer.maybeDeferred(self.service.stopService),
816+ ]
817+ )
818
819 def log_stopped(ignored):
820 notify(events.ServerStopped())
821diff --git a/src/lazr/sshserver/session.py b/src/lazr/sshserver/session.py
822index 44c20a2..29bde10 100644
823--- a/src/lazr/sshserver/session.py
824+++ b/src/lazr/sshserver/session.py
825@@ -7,16 +7,12 @@ from __future__ import absolute_import, print_function
826
827 __metaclass__ = type
828 __all__ = [
829- 'DoNothingSession',
830- 'PatchedSSHSession',
831- ]
832+ "DoNothingSession",
833+ "PatchedSSHSession",
834+]
835
836 from twisted.conch.interfaces import ISession
837-from twisted.conch.ssh import (
838- channel,
839- connection,
840- session,
841- )
842+from twisted.conch.ssh import channel, connection, session
843 from zope.interface import implementer
844
845
846@@ -42,7 +38,7 @@ class PatchedSSHSession(session.SSHSession, object):
847 # transport even if it's None. I don't know *why* it is None, so this
848 # doesn't necessarily address the root cause.
849 # See http://twistedmatrix.com/trac/ticket/2754.
850- transport = getattr(self.client, 'transport', None)
851+ transport = getattr(self.client, "transport", None)
852 if transport is not None:
853 transport.loseConnection()
854 # This is called by session.SSHSession.loseConnection. SSHChannel is
855@@ -59,12 +55,12 @@ class PatchedSSHSession(session.SSHSession, object):
856 # self.client.transport is entirely paranoia inspired by the comment
857 # in `loseConnection` above. It would be good to know if and why it is
858 # necessary. See http://twistedmatrix.com/trac/ticket/2754.
859- transport = getattr(self.client, 'transport', None)
860+ transport = getattr(self.client, "transport", None)
861 if transport is not None:
862 # For SFTP connections, 'transport' is actually a _DummyTransport
863 # instance. Neither _DummyTransport nor the protocol it wraps
864 # (filetransfer.FileTransferServer) support pausing.
865- pauseProducing = getattr(transport, 'pauseProducing', None)
866+ pauseProducing = getattr(transport, "pauseProducing", None)
867 if pauseProducing is not None:
868 pauseProducing()
869
870@@ -78,12 +74,12 @@ class PatchedSSHSession(session.SSHSession, object):
871 # self.client.transport is entirely paranoia inspired by the comment
872 # in `loseConnection` above. It would be good to know if and why it is
873 # necessary. See http://twistedmatrix.com/trac/ticket/2754.
874- transport = getattr(self.client, 'transport', None)
875+ transport = getattr(self.client, "transport", None)
876 if transport is not None:
877 # For SFTP connections, 'transport' is actually a _DummyTransport
878 # instance. Neither _DummyTransport nor the protocol it wraps
879 # (filetransfer.FileTransferServer) support pausing.
880- resumeProducing = getattr(transport, 'resumeProducing', None)
881+ resumeProducing = getattr(transport, "resumeProducing", None)
882 if resumeProducing is not None:
883 resumeProducing()
884
885@@ -102,14 +98,14 @@ class DoNothingSession:
886 """See ISession."""
887
888 def errorWithMessage(self, protocol, msg):
889- protocol.session.writeExtended(
890- connection.EXTENDED_DATA_STDERR, msg)
891+ protocol.session.writeExtended(connection.EXTENDED_DATA_STDERR, msg)
892 protocol.loseConnection()
893
894 def execCommand(self, protocol, command):
895 """See ISession."""
896 self.errorWithMessage(
897- protocol, "Not allowed to execute commands on this server.\r\n")
898+ protocol, "Not allowed to execute commands on this server.\r\n"
899+ )
900
901 def getPty(self, term, windowSize, modes):
902 """See ISession."""
903diff --git a/src/lazr/sshserver/sftp.py b/src/lazr/sshserver/sftp.py
904index 64c54c7..1335ce8 100644
905--- a/src/lazr/sshserver/sftp.py
906+++ b/src/lazr/sshserver/sftp.py
907@@ -7,9 +7,9 @@ from __future__ import absolute_import, print_function
908
909 __metaclass__ = type
910 __all__ = [
911- 'FileIsADirectory',
912- 'FileTransferServer',
913- ]
914+ "FileIsADirectory",
915+ "FileTransferServer",
916+]
917
918 from twisted.conch.ssh import filetransfer
919 from zope.event import notify
920@@ -26,7 +26,8 @@ class FileIsADirectory(Exception):
921 def __init__(self, path, extra=None):
922 self.path = path
923 super(FileIsADirectory, self).__init__(
924- "File is a directory: %r" % path)
925+ "File is a directory: %r" % path
926+ )
927
928
929 class FileTransferServer(filetransfer.FileTransferServer):
930diff --git a/src/lazr/sshserver/tests/test_accesslog.py b/src/lazr/sshserver/tests/test_accesslog.py
931index 5b8a11a..5dad2f6 100644
932--- a/src/lazr/sshserver/tests/test_accesslog.py
933+++ b/src/lazr/sshserver/tests/test_accesslog.py
934@@ -8,10 +8,10 @@ from __future__ import absolute_import, print_function
935 __metaclass__ = type
936
937 import logging
938-from logging.handlers import WatchedFileHandler
939 import os
940 import sys
941 import tempfile
942+from logging.handlers import WatchedFileHandler
943
944 try:
945 from bzrlib.tests import TestCase as BzrTestCase
946@@ -31,11 +31,12 @@ class LoggingManagerMixin:
947 def makeLogger(self, name=None):
948 if name is None:
949 self._log_count += 1
950- name = '%s-%s' % (self.id().split('.')[-1], self._log_count)
951+ name = "%s-%s" % (self.id().split(".")[-1], self._log_count)
952 return logging.getLogger(name)
953
954- def installLoggingManager(self, main_log=None, access_log=None,
955- access_log_path=None):
956+ def installLoggingManager(
957+ self, main_log=None, access_log=None, access_log_path=None
958+ ):
959 if main_log is None:
960 main_log = self.makeLogger()
961 if access_log is None:
962@@ -51,17 +52,17 @@ class LoggingManagerMixin:
963
964
965 if BzrTestCase is not None:
966- class TestLoggingBazaarInteraction(BzrTestCase, LoggingManagerMixin):
967
968+ class TestLoggingBazaarInteraction(BzrTestCase, LoggingManagerMixin):
969 def test_leaves_bzr_handlers_unchanged(self):
970 # Bazaar's log handling is untouched by logging setup.
971- root_handlers = logging.getLogger('').handlers
972- bzr_handlers = logging.getLogger('bzr').handlers
973+ root_handlers = logging.getLogger("").handlers
974+ bzr_handlers = logging.getLogger("bzr").handlers
975
976 self.apply_redirected(a_callable=self.installLoggingManager)
977
978- self.assertEqual(root_handlers, logging.getLogger('').handlers)
979- self.assertEqual(bzr_handlers, logging.getLogger('bzr').handlers)
980+ self.assertEqual(root_handlers, logging.getLogger("").handlers)
981+ self.assertEqual(bzr_handlers, logging.getLogger("bzr").handlers)
982
983 def test_log_doesnt_go_to_stderr(self):
984 # Once logging setup is called, any messages logged to the
985@@ -73,15 +74,15 @@ if BzrTestCase is not None:
986 self.installLoggingManager(log)
987
988 # Make sure that a logged message does not go to stderr.
989- log.info('Hello hello')
990- self.assertEqual(sys.stderr.getvalue(), '')
991+ log.info("Hello hello")
992+ self.assertEqual(sys.stderr.getvalue(), "")
993
994 self.apply_redirected(a_callable=inner)
995
996
997 class TestLoggingManager(
998- testtools.TestCase, fixtures.TestWithFixtures, LoggingManagerMixin):
999-
1000+ testtools.TestCase, fixtures.TestWithFixtures, LoggingManagerMixin
1001+):
1002 def test_main_log_handlers(self):
1003 # There needs to be at least one handler for the root logger. If there
1004 # isn't, we'll get constant errors complaining about the lack of
1005@@ -93,10 +94,9 @@ class TestLoggingManager(
1006
1007 def _get_handlers(self):
1008 registrations = (
1009- zope.component.getGlobalSiteManager().registeredHandlers())
1010- return [
1011- registration.factory
1012- for registration in registrations]
1013+ zope.component.getGlobalSiteManager().registeredHandlers()
1014+ )
1015+ return [registration.factory for registration in registrations]
1016
1017 def test_set_up_registers_event_handler(self):
1018 manager = self.installLoggingManager()
1019@@ -136,10 +136,10 @@ class TestLoggingManager(
1020 # to the SSH server access log.
1021 directory = self.useFixture(fixtures.TempDir()).path
1022 access_log = self.makeLogger()
1023- access_log_path = os.path.join(directory, 'access.log')
1024+ access_log_path = os.path.join(directory, "access.log")
1025 self.installLoggingManager(
1026- access_log=access_log,
1027- access_log_path=access_log_path)
1028+ access_log=access_log, access_log_path=access_log_path
1029+ )
1030 [handler] = access_log.handlers
1031 self.assertIsInstance(handler, WatchedFileHandler)
1032 self.assertEqual(access_log_path, handler.baseFilename)
1033diff --git a/src/lazr/sshserver/tests/test_auth.py b/src/lazr/sshserver/tests/test_auth.py
1034index d146a19..7fb7f21 100644
1035--- a/src/lazr/sshserver/tests/test_auth.py
1036+++ b/src/lazr/sshserver/tests/test_auth.py
1037@@ -13,31 +13,19 @@ import sys
1038
1039 import testtools
1040 from testtools.deferredruntest import (
1041- assert_fails_with,
1042 AsynchronousDeferredRunTest,
1043+ assert_fails_with,
1044 flush_logged_errors,
1045- )
1046+)
1047 from twisted.conch.error import ConchError
1048 from twisted.conch.ssh import userauth
1049-from twisted.conch.ssh.common import (
1050- getNS,
1051- NS,
1052- )
1053-from twisted.conch.ssh.keys import (
1054- BadKeyError,
1055- Key,
1056- )
1057-from twisted.conch.ssh.transport import (
1058- SSHCiphers,
1059- SSHServerTransport,
1060- )
1061+from twisted.conch.ssh.common import NS, getNS
1062+from twisted.conch.ssh.keys import BadKeyError, Key
1063+from twisted.conch.ssh.transport import SSHCiphers, SSHServerTransport
1064 from twisted.cred.checkers import ICredentialsChecker
1065 from twisted.cred.credentials import ISSHPrivateKey
1066 from twisted.cred.error import UnauthorizedLogin
1067-from twisted.cred.portal import (
1068- IRealm,
1069- Portal,
1070- )
1071+from twisted.cred.portal import IRealm, Portal
1072 from twisted.internet import defer
1073 from twisted.python import failure
1074 from twisted.python.util import sibpath
1075@@ -47,8 +35,8 @@ from lazr.sshserver import auth
1076
1077
1078 def suppress_stderr(function):
1079- """Deferred friendly decorator that suppresses output from a function.
1080- """
1081+ """Deferred friendly decorator that suppresses output from a function."""
1082+
1083 def set_stderr(result, stream):
1084 sys.stderr = stream
1085 return result
1086@@ -77,10 +65,12 @@ class MockRealm:
1087
1088 def requestAvatar(self, avatar_id, mind, *interfaces):
1089 user_dict = {
1090- 'id': avatar_id, 'name': avatar_id, 'teams': [],
1091- 'initialBranches': []}
1092- return (
1093- interfaces[0], auth.LaunchpadAvatar(user_dict), lambda: None)
1094+ "id": avatar_id,
1095+ "name": avatar_id,
1096+ "teams": [],
1097+ "initialBranches": [],
1098+ }
1099+ return (interfaces[0], auth.LaunchpadAvatar(user_dict), lambda: None)
1100
1101
1102 class MockSSHTransport(SSHServerTransport):
1103@@ -103,7 +93,7 @@ class MockSSHTransport(SSHServerTransport):
1104 # In Twisted 8.0.1, Conch's transport starts referring to
1105 # currentEncryptions where it didn't before. Provide a dummy value for
1106 # it.
1107- self.currentEncryptions = SSHCiphers('none', 'none', 'none', 'none')
1108+ self.currentEncryptions = SSHCiphers("none", "none", "none", "none")
1109 self.packets = []
1110 self.factory = self.Factory()
1111 self.factory.portal = portal
1112@@ -131,25 +121,27 @@ class UserAuthServerMixin(object):
1113 messages = userauth.SSHUserAuthServer.protocolMessages
1114 self.assertEqual(
1115 [messages[msg_type] for msg_type in message_types],
1116- [messages[packet_type]
1117- for packet_type, contents in self.transport.packets])
1118+ [
1119+ messages[packet_type]
1120+ for packet_type, contents in self.transport.packets
1121+ ],
1122+ )
1123
1124- def assertBannerSent(self, banner_message, expected_language='en'):
1125+ def assertBannerSent(self, banner_message, expected_language="en"):
1126 """Assert that 'banner_message' was sent as an SSH banner."""
1127 # Check that we received a BANNER, then a FAILURE.
1128 for packet_type, packet_content in self.transport.packets:
1129 if packet_type == userauth.MSG_USERAUTH_BANNER:
1130 bytes, language, empty = getNS(packet_content, 2)
1131- self.assertEqual(banner_message, bytes.decode('UTF8'))
1132- self.assertEqual(expected_language, language.decode('UTF8'))
1133- self.assertEqual(b'', empty)
1134+ self.assertEqual(banner_message, bytes.decode("UTF8"))
1135+ self.assertEqual(expected_language, language.decode("UTF8"))
1136+ self.assertEqual(b"", empty)
1137 break
1138 else:
1139 self.fail("No banner logged.")
1140
1141
1142 class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase):
1143-
1144 def test_sendBanner(self):
1145 # sendBanner should send an SSH 'packet' with type MSG_USERAUTH_BANNER
1146 # and two fields. The first field is the message itself, and the
1147@@ -160,11 +152,13 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase):
1148 #
1149 # See RFC 4252, Section 5.4.
1150 message = u"test message"
1151- self.user_auth.sendBanner(message, language='en-US')
1152- self.assertBannerSent(message + '\r\n', 'en-US')
1153+ self.user_auth.sendBanner(message, language="en-US")
1154+ self.assertBannerSent(message + "\r\n", "en-US")
1155 self.assertEqual(
1156- 1, len(self.transport.packets),
1157- "More than just banner was sent: %r" % self.transport.packets)
1158+ 1,
1159+ len(self.transport.packets),
1160+ "More than just banner was sent: %r" % self.transport.packets,
1161+ )
1162
1163 def test_sendBannerUsesCRLF(self):
1164 # sendBanner should make sure that any line breaks in the message are
1165@@ -174,7 +168,7 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase):
1166 self.user_auth.sendBanner(u"test\nmessage")
1167 [(messageType, payload)] = self.transport.packets
1168 bytes, language, empty = getNS(payload, 2)
1169- self.assertEqual(bytes.decode('UTF8'), u"test\r\nmessage\r\n")
1170+ self.assertEqual(bytes.decode("UTF8"), u"test\r\nmessage\r\n")
1171
1172 def test_requestRaisesConchError(self):
1173 # ssh_USERAUTH_REQUEST should raise a ConchError if tryAuth returns
1174@@ -188,11 +182,13 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase):
1175
1176 tryAuth = self.user_auth.tryAuth
1177 self.user_auth.tryAuth = mock_try_auth
1178- _ebBadAuth, self.user_auth._ebBadAuth = (self.user_auth._ebBadAuth,
1179- mock_eb_bad_auth)
1180+ _ebBadAuth, self.user_auth._ebBadAuth = (
1181+ self.user_auth._ebBadAuth,
1182+ mock_eb_bad_auth,
1183+ )
1184 self.user_auth.serviceStarted()
1185 try:
1186- packet = NS(b'jml') + NS(b'foo') + NS(b'public_key') + NS(b'data')
1187+ packet = NS(b"jml") + NS(b"foo") + NS(b"public_key") + NS(b"data")
1188 self.user_auth.ssh_USERAUTH_REQUEST(packet)
1189 finally:
1190 self.user_auth.serviceStopped()
1191@@ -202,20 +198,25 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase):
1192 def test_handlesBadKey(self):
1193 # auth_publickey handles the case where Twisted fails to load the
1194 # user-supplied public key.
1195- self.transport.sessionID = ''
1196+ self.transport.sessionID = ""
1197 packet = (
1198- NS(b'user') + NS(b'') + NS(b'publickey') +
1199- b'\x01' + NS(b'ssh-rsa') + NS(b'badkey') +
1200- NS(NS(b'ssh-rsa') + NS(b'\x01' * 128)))
1201+ NS(b"user")
1202+ + NS(b"")
1203+ + NS(b"publickey")
1204+ + b"\x01"
1205+ + NS(b"ssh-rsa")
1206+ + NS(b"badkey")
1207+ + NS(NS(b"ssh-rsa") + NS(b"\x01" * 128))
1208+ )
1209 self.user_auth.serviceStarted()
1210- self.user_auth.supportedAuthentications.append(b'publickey')
1211+ self.user_auth.supportedAuthentications.append(b"publickey")
1212 try:
1213 self.user_auth.ssh_USERAUTH_REQUEST(packet)
1214 [(packet_type, packet_content)] = self.transport.packets
1215 self.assertEqual(userauth.MSG_USERAUTH_FAILURE, packet_type)
1216 name_list, partial_success = getNS(packet_content)
1217- self.assertEqual(b'publickey', name_list)
1218- self.assertEqual(b'\x00', partial_success)
1219+ self.assertEqual(b"publickey", name_list)
1220+ self.assertEqual(b"\x00", partial_success)
1221 finally:
1222 self.user_auth.serviceStopped()
1223
1224@@ -229,19 +230,27 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase):
1225
1226 def __call__(self, credentials, mind, *interfaces):
1227 self.credentials = credentials
1228- return defer.succeed(self.portal.realm.requestAvatar(
1229- credentials.username, mind, *interfaces))
1230+ return defer.succeed(
1231+ self.portal.realm.requestAvatar(
1232+ credentials.username, mind, *interfaces
1233+ )
1234+ )
1235
1236- self.transport.sessionID = ''
1237+ self.transport.sessionID = ""
1238 self.portal.login = MockLogin(self.portal)
1239- keydir = sibpath(__file__, 'keys')
1240- with open(os.path.join(keydir, 'ssh_host_key_rsa.pub'), 'rb') as f:
1241+ keydir = sibpath(__file__, "keys")
1242+ with open(os.path.join(keydir, "ssh_host_key_rsa.pub"), "rb") as f:
1243 public_key = Key.fromString(f.read())
1244 packet = (
1245- NS(b'user') + NS(b'') + NS(b'publickey') +
1246- b'\x00' + NS(b'ssh-rsa') + NS(public_key.blob()))
1247+ NS(b"user")
1248+ + NS(b"")
1249+ + NS(b"publickey")
1250+ + b"\x00"
1251+ + NS(b"ssh-rsa")
1252+ + NS(public_key.blob())
1253+ )
1254 self.user_auth.serviceStarted()
1255- self.user_auth.supportedAuthentications.append(b'publickey')
1256+ self.user_auth.supportedAuthentications.append(b"publickey")
1257 try:
1258 self.user_auth.ssh_USERAUTH_REQUEST(packet)
1259 self.assertIsNone(self.portal.login.credentials.signature)
1260@@ -257,27 +266,36 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase):
1261
1262 def __call__(self, credentials, mind, *interfaces):
1263 self.credentials = credentials
1264- return defer.succeed(self.portal.realm.requestAvatar(
1265- credentials.username, mind, *interfaces))
1266+ return defer.succeed(
1267+ self.portal.realm.requestAvatar(
1268+ credentials.username, mind, *interfaces
1269+ )
1270+ )
1271
1272- self.transport.sessionID = ''
1273+ self.transport.sessionID = ""
1274 self.portal.login = MockLogin(self.portal)
1275- keydir = sibpath(__file__, 'keys')
1276- with open(os.path.join(keydir, 'ssh_host_key_rsa.pub'), 'rb') as f:
1277+ keydir = sibpath(__file__, "keys")
1278+ with open(os.path.join(keydir, "ssh_host_key_rsa.pub"), "rb") as f:
1279 public_key = Key.fromString(f.read())
1280 public_key_len = (public_key.size() + 7) // 8
1281- signature = b'\x01' * (public_key_len - 1)
1282+ signature = b"\x01" * (public_key_len - 1)
1283 packet = (
1284- NS(b'user') + NS(b'') + NS(b'publickey') +
1285- b'\x01' + NS(b'ssh-rsa') + NS(public_key.blob()) +
1286- NS(NS(b'ssh-rsa') + NS(signature)))
1287+ NS(b"user")
1288+ + NS(b"")
1289+ + NS(b"publickey")
1290+ + b"\x01"
1291+ + NS(b"ssh-rsa")
1292+ + NS(public_key.blob())
1293+ + NS(NS(b"ssh-rsa") + NS(signature))
1294+ )
1295 self.user_auth.serviceStarted()
1296- self.user_auth.supportedAuthentications.append(b'publickey')
1297+ self.user_auth.supportedAuthentications.append(b"publickey")
1298 try:
1299 self.user_auth.ssh_USERAUTH_REQUEST(packet)
1300 self.assertEqual(
1301- NS(b'ssh-rsa') + NS(b'\x00' + signature),
1302- self.portal.login.credentials.signature)
1303+ NS(b"ssh-rsa") + NS(b"\x00" + signature),
1304+ self.portal.login.credentials.signature,
1305+ )
1306 finally:
1307 self.user_auth.serviceStopped()
1308
1309@@ -292,14 +310,15 @@ class MockChecker:
1310
1311 credentialInterfaces = (ISSHPrivateKey,)
1312
1313- error_message = u'error message'
1314+ error_message = u"error message"
1315
1316 def requestAvatarId(self, credentials):
1317- if credentials.username == b'success':
1318+ if credentials.username == b"success":
1319 return credentials.username
1320 else:
1321 return failure.Failure(
1322- auth.UserDisplayedUnauthorizedLogin(self.error_message))
1323+ auth.UserDisplayedUnauthorizedLogin(self.error_message)
1324+ )
1325
1326
1327 class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase):
1328@@ -327,27 +346,30 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase):
1329 super(TestAuthenticationBannerDisplay, self).tearDown()
1330
1331 def _makeKey(self):
1332- keydir = sibpath(__file__, 'keys')
1333- with open(os.path.join(keydir, 'ssh_host_key_rsa.pub'), 'rb') as f:
1334+ keydir = sibpath(__file__, "keys")
1335+ with open(os.path.join(keydir, "ssh_host_key_rsa.pub"), "rb") as f:
1336 public_key = Key.fromString(f.read())
1337 if isinstance(public_key, str):
1338- return b'\x00' + NS(b'rsa') + NS(public_key)
1339+ return b"\x00" + NS(b"rsa") + NS(public_key)
1340 else:
1341- return b'\x00' + NS(b'rsa') + NS(public_key.blob())
1342+ return b"\x00" + NS(b"rsa") + NS(public_key.blob())
1343
1344 def requestFailedAuthentication(self):
1345 return self.user_auth.ssh_USERAUTH_REQUEST(
1346- NS(b'failure') + NS(b'') + NS(b'publickey') + self.key_data)
1347+ NS(b"failure") + NS(b"") + NS(b"publickey") + self.key_data
1348+ )
1349
1350 def requestSuccessfulAuthentication(self):
1351 return self.user_auth.ssh_USERAUTH_REQUEST(
1352- NS(b'success') + NS(b'') + NS(b'publickey') + self.key_data)
1353+ NS(b"success") + NS(b"") + NS(b"publickey") + self.key_data
1354+ )
1355
1356 def requestUnsupportedAuthentication(self):
1357 # Note that it doesn't matter how the checker responds -- the server
1358 # doesn't get that far.
1359 return self.user_auth.ssh_USERAUTH_REQUEST(
1360- NS(b'success') + NS(b'') + NS(b'none') + NS(b''))
1361+ NS(b"success") + NS(b"") + NS(b"none") + NS(b"")
1362+ )
1363
1364 @defer.inlineCallbacks
1365 def test_bannerNotSentOnSuccess(self):
1366@@ -366,8 +388,9 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase):
1367 self.user_auth._banner = "Boogedy boo"
1368 yield self.requestSuccessfulAuthentication()
1369 self.assertMessageOrder(
1370- [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_SUCCESS])
1371- self.assertBannerSent(self.user_auth._banner + '\r\n')
1372+ [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_SUCCESS]
1373+ )
1374+ self.assertBannerSent(self.user_auth._banner + "\r\n")
1375
1376 @defer.inlineCallbacks
1377 def test_defaultBannerSentOnlyOnce(self):
1378@@ -381,9 +404,13 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase):
1379
1380 # Check that no banner was sent to the user.
1381 self.assertMessageOrder(
1382- [userauth.MSG_USERAUTH_FAILURE, userauth.MSG_USERAUTH_BANNER,
1383- userauth.MSG_USERAUTH_SUCCESS])
1384- self.assertBannerSent(self.user_auth._banner + '\r\n')
1385+ [
1386+ userauth.MSG_USERAUTH_FAILURE,
1387+ userauth.MSG_USERAUTH_BANNER,
1388+ userauth.MSG_USERAUTH_SUCCESS,
1389+ ]
1390+ )
1391+ self.assertBannerSent(self.user_auth._banner + "\r\n")
1392
1393 @defer.inlineCallbacks
1394 def test_defaultBannerNotSentOnFailure(self):
1395@@ -394,8 +421,9 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase):
1396 yield self.requestFailedAuthentication()
1397
1398 self.assertMessageOrder(
1399- [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE])
1400- self.assertBannerSent(MockChecker.error_message + '\r\n')
1401+ [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE]
1402+ )
1403+ self.assertBannerSent(MockChecker.error_message + "\r\n")
1404
1405 @defer.inlineCallbacks
1406 def test_loggedToBanner(self):
1407@@ -404,8 +432,9 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase):
1408 yield self.requestFailedAuthentication()
1409 # Check that we received a BANNER, then a FAILURE.
1410 self.assertMessageOrder(
1411- [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE])
1412- self.assertBannerSent(MockChecker.error_message + '\r\n')
1413+ [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE]
1414+ )
1415+ self.assertBannerSent(MockChecker.error_message + "\r\n")
1416
1417 @defer.inlineCallbacks
1418 def test_unsupportedAuthMethodNotLogged(self):
1419@@ -431,40 +460,45 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1420 run_tests_with = AsynchronousDeferredRunTest
1421
1422 class FakeAuthenticationEndpoint:
1423- """A fake client for enough of `IAuthServer` for this test.
1424- """
1425+ """A fake client for enough of `IAuthServer` for this test."""
1426
1427- valid_user = 'valid_user'
1428- no_key_user = 'no_key_user'
1429- valid_key_rsa = b'valid_key_rsa'
1430- valid_key_dsa = b'valid_key_dsa'
1431- valid_key_ecdsa = b'valid_key_ecdsa'
1432- valid_key_ed25519 = b'valid_key_ed25519'
1433+ valid_user = "valid_user"
1434+ no_key_user = "no_key_user"
1435+ valid_key_rsa = b"valid_key_rsa"
1436+ valid_key_dsa = b"valid_key_dsa"
1437+ valid_key_ecdsa = b"valid_key_ecdsa"
1438+ valid_key_ed25519 = b"valid_key_ed25519"
1439
1440 def __init__(self):
1441 self.calls = []
1442
1443 def callRemote(self, function_name, *args, **kwargs):
1444- return getattr(
1445- self, 'xmlrpc_%s' % function_name)(*args, **kwargs)
1446+ return getattr(self, "xmlrpc_%s" % function_name)(*args, **kwargs)
1447
1448 def xmlrpc_getUserAndSSHKeys(self, username):
1449 self.calls.append(username)
1450 if username == self.valid_user:
1451- return defer.succeed({
1452- 'name': username,
1453- 'keys': [
1454- ('RSA', base64.b64encode(self.valid_key_rsa)),
1455- ('DSA', base64.b64encode(self.valid_key_dsa)),
1456- ('ECDSA', base64.b64encode(self.valid_key_ecdsa)),
1457- ('ED25519', base64.b64encode(self.valid_key_ed25519)),
1458+ return defer.succeed(
1459+ {
1460+ "name": username,
1461+ "keys": [
1462+ ("RSA", base64.b64encode(self.valid_key_rsa)),
1463+ ("DSA", base64.b64encode(self.valid_key_dsa)),
1464+ ("ECDSA", base64.b64encode(self.valid_key_ecdsa)),
1465+ (
1466+ "ED25519",
1467+ base64.b64encode(self.valid_key_ed25519),
1468+ ),
1469 ],
1470- })
1471+ }
1472+ )
1473 elif username == self.no_key_user:
1474- return defer.succeed({
1475- 'name': username,
1476- 'keys': [],
1477- })
1478+ return defer.succeed(
1479+ {
1480+ "name": username,
1481+ "keys": [],
1482+ }
1483+ )
1484 else:
1485 try:
1486 raise auth.NoSuchPersonWithName(username)
1487@@ -475,7 +509,8 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1488 if mind is None:
1489 mind = auth.UserDetailsMind()
1490 return auth.SSHPrivateKeyWithMind(
1491- username, key_type, public_key, '', None, mind)
1492+ username, key_type, public_key, "", None, mind
1493+ )
1494
1495 def makeChecker(self, do_signature_checking=False):
1496 """Construct a PublicKeyFromLaunchpadChecker.
1497@@ -502,27 +537,32 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1498 # Attempting to log in with a username and key known to the
1499 # authentication end-point succeeds.
1500 for key_type, public_key in (
1501- (b'ssh-rsa', self.authserver.valid_key_rsa),
1502- (b'ssh-dss', self.authserver.valid_key_dsa),
1503- (b'ecdsa-sha2-nistp256', self.authserver.valid_key_ecdsa),
1504- (b'ssh-ed25519', self.authserver.valid_key_ed25519),
1505- ):
1506+ (b"ssh-rsa", self.authserver.valid_key_rsa),
1507+ (b"ssh-dss", self.authserver.valid_key_dsa),
1508+ (b"ecdsa-sha2-nistp256", self.authserver.valid_key_ecdsa),
1509+ (b"ssh-ed25519", self.authserver.valid_key_ed25519),
1510+ ):
1511 creds = self.makeCredentials(
1512- self.authserver.valid_user.encode('UTF-8'), key_type,
1513- public_key)
1514+ self.authserver.valid_user.encode("UTF-8"),
1515+ key_type,
1516+ public_key,
1517+ )
1518 checker = self.makeChecker()
1519 username = yield checker.requestAvatarId(creds)
1520 self.assertEqual(
1521- self.authserver.valid_user.encode('UTF-8'), username)
1522+ self.authserver.valid_user.encode("UTF-8"), username
1523+ )
1524
1525 @suppress_stderr
1526 def test_invalid_signature(self):
1527 # The checker requests attempts to authenticate if the requests have
1528 # an invalid signature.
1529 creds = self.makeCredentials(
1530- self.authserver.valid_user.encode('UTF-8'), b'ssh-dss',
1531- self.authserver.valid_key_dsa)
1532- creds.signature = 'a'
1533+ self.authserver.valid_user.encode("UTF-8"),
1534+ b"ssh-dss",
1535+ self.authserver.valid_key_dsa,
1536+ )
1537+ creds.signature = "a"
1538 checker = self.makeChecker(True)
1539 d = checker.requestAvatarId(creds)
1540
1541@@ -545,19 +585,22 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1542 :return: Deferred. You must return this from your test.
1543 """
1544 d = assert_fails_with(
1545- checker.requestAvatarId(creds),
1546- auth.UserDisplayedUnauthorizedLogin)
1547+ checker.requestAvatarId(creds), auth.UserDisplayedUnauthorizedLogin
1548+ )
1549 d.addCallback(
1550- lambda exception: self.assertEqual(str(exception), error_message))
1551+ lambda exception: self.assertEqual(str(exception), error_message)
1552+ )
1553 return d
1554
1555 def test_badUserEncoding(self):
1556 # Attempting to sign in using a non-UTF-8 user name fails.
1557 checker = self.makeChecker()
1558 creds = self.makeCredentials(
1559- b'\x80', b'ssh-dss', self.authserver.valid_key_dsa)
1560+ b"\x80", b"ssh-dss", self.authserver.valid_key_dsa
1561+ )
1562 return self.assertLoginError(
1563- checker, creds, 'No such Launchpad account: %r' % b'\x80')
1564+ checker, creds, "No such Launchpad account: %r" % b"\x80"
1565+ )
1566
1567 def test_noSuchUser(self):
1568 # When someone signs in with a non-existent user, they should be told
1569@@ -565,21 +608,27 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1570 # Launchpad user names is public.
1571 checker = self.makeChecker()
1572 creds = self.makeCredentials(
1573- b'no-such-user', b'ssh-dss', self.authserver.valid_key_dsa)
1574+ b"no-such-user", b"ssh-dss", self.authserver.valid_key_dsa
1575+ )
1576 return self.assertLoginError(
1577- checker, creds, 'No such Launchpad account: no-such-user')
1578+ checker, creds, "No such Launchpad account: no-such-user"
1579+ )
1580
1581 def test_noKeys(self):
1582 # When you sign into an existing account with no SSH keys, the SSH
1583 # server informs you that the account has no keys.
1584 checker = self.makeChecker()
1585 creds = self.makeCredentials(
1586- self.authserver.no_key_user.encode('UTF-8'), b'ssh-dss',
1587- self.authserver.valid_key_dsa)
1588+ self.authserver.no_key_user.encode("UTF-8"),
1589+ b"ssh-dss",
1590+ self.authserver.valid_key_dsa,
1591+ )
1592 return self.assertLoginError(
1593- checker, creds,
1594+ checker,
1595+ creds,
1596 "Launchpad user '%s' doesn't have a registered SSH key"
1597- % self.authserver.no_key_user)
1598+ % self.authserver.no_key_user,
1599+ )
1600
1601 def test_wrongKey(self):
1602 # When you sign into an existing account using the wrong key, you
1603@@ -587,34 +636,40 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1604 # tries several keys as part of normal operation.
1605 checker = self.makeChecker()
1606 creds = self.makeCredentials(
1607- self.authserver.valid_user.encode('UTF-8'), b'ssh-dss',
1608- b'invalid key')
1609+ self.authserver.valid_user.encode("UTF-8"),
1610+ b"ssh-dss",
1611+ b"invalid key",
1612+ )
1613 # We cannot use assertLoginError because we are checking that we fail
1614 # with UnauthorizedLogin and not its subclass
1615 # UserDisplayedUnauthorizedLogin.
1616 d = assert_fails_with(
1617- checker.requestAvatarId(creds),
1618- UnauthorizedLogin)
1619+ checker.requestAvatarId(creds), UnauthorizedLogin
1620+ )
1621 d.addCallback(
1622- lambda exception:
1623- self.assertFalse(
1624+ lambda exception: self.assertFalse(
1625 isinstance(exception, auth.UserDisplayedUnauthorizedLogin),
1626- "Should not be a UserDisplayedUnauthorizedLogin"))
1627+ "Should not be a UserDisplayedUnauthorizedLogin",
1628+ )
1629+ )
1630 return d
1631
1632 @defer.inlineCallbacks
1633 def test_unknownKeyType(self):
1634 # Authenticating using a key with an unknown type fails.
1635 creds = self.makeCredentials(
1636- self.authserver.valid_user.encode('UTF-8'), b'nonsense',
1637- self.authserver.valid_key_rsa)
1638+ self.authserver.valid_user.encode("UTF-8"),
1639+ b"nonsense",
1640+ self.authserver.valid_key_rsa,
1641+ )
1642 checker = self.makeChecker()
1643 exception = yield assert_fails_with(
1644- checker.requestAvatarId(creds),
1645- UnauthorizedLogin)
1646+ checker.requestAvatarId(creds), UnauthorizedLogin
1647+ )
1648 self.assertFalse(
1649 isinstance(exception, auth.UserDisplayedUnauthorizedLogin),
1650- "Should not be a UserDisplayedUnauthorizedLogin")
1651+ "Should not be a UserDisplayedUnauthorizedLogin",
1652+ )
1653
1654 @defer.inlineCallbacks
1655 def test_successful_with_second_key_calls_authserver_once(self):
1656@@ -625,16 +680,22 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1657 checker = self.makeChecker()
1658 mind = auth.UserDetailsMind()
1659 wrong_key_creds = self.makeCredentials(
1660- self.authserver.valid_user.encode('UTF-8'), b'ssh-dss',
1661- b'invalid key', mind)
1662+ self.authserver.valid_user.encode("UTF-8"),
1663+ b"ssh-dss",
1664+ b"invalid key",
1665+ mind,
1666+ )
1667 right_key_creds = self.makeCredentials(
1668- self.authserver.valid_user.encode('UTF-8'), b'ssh-dss',
1669- self.authserver.valid_key_dsa, mind)
1670+ self.authserver.valid_user.encode("UTF-8"),
1671+ b"ssh-dss",
1672+ self.authserver.valid_key_dsa,
1673+ mind,
1674+ )
1675 try:
1676 username = yield checker.requestAvatarId(wrong_key_creds)
1677 except UnauthorizedLogin:
1678 username = yield checker.requestAvatarId(right_key_creds)
1679- self.assertEqual(self.authserver.valid_user.encode('UTF-8'), username)
1680+ self.assertEqual(self.authserver.valid_user.encode("UTF-8"), username)
1681 self.assertEqual([self.authserver.valid_user], self.authserver.calls)
1682
1683 def test_noSuchUser_with_two_keys_calls_authserver_once(self):
1684@@ -643,21 +704,22 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase):
1685 checker = self.makeChecker()
1686 mind = auth.UserDetailsMind()
1687 creds_1 = self.makeCredentials(
1688- b'invalid-user', b'ssh-dss', b'invalid key 1', mind)
1689+ b"invalid-user", b"ssh-dss", b"invalid key 1", mind
1690+ )
1691 creds_2 = self.makeCredentials(
1692- b'invalid-user', b'ssh-dss', b'invalid key 2', mind)
1693+ b"invalid-user", b"ssh-dss", b"invalid key 2", mind
1694+ )
1695 d = checker.requestAvatarId(creds_1)
1696
1697 def try_second_key(failure):
1698 return assert_fails_with(
1699- checker.requestAvatarId(creds_2),
1700- UnauthorizedLogin)
1701+ checker.requestAvatarId(creds_2), UnauthorizedLogin
1702+ )
1703
1704 d.addErrback(try_second_key)
1705
1706 def check_one_call(r):
1707- self.assertEqual(
1708- ['invalid-user'], self.authserver.calls)
1709+ self.assertEqual(["invalid-user"], self.authserver.calls)
1710 return r
1711
1712 d.addCallback(check_one_call)
1713diff --git a/src/lazr/sshserver/tests/test_docs.py b/src/lazr/sshserver/tests/test_docs.py
1714index a331bc7..c498504 100644
1715--- a/src/lazr/sshserver/tests/test_docs.py
1716+++ b/src/lazr/sshserver/tests/test_docs.py
1717@@ -21,39 +21,48 @@ from __future__ import absolute_import, print_function
1718
1719 __metaclass__ = type
1720 __all__ = [
1721- 'load_tests',
1722- ]
1723+ "load_tests",
1724+]
1725
1726 import __future__
1727+
1728 import atexit
1729 import doctest
1730 import os
1731+
1732 # pylint: disable-msg=F0401
1733 from pkg_resources import (
1734- resource_filename, resource_exists, resource_listdir, cleanup_resources)
1735-
1736+ cleanup_resources,
1737+ resource_exists,
1738+ resource_filename,
1739+ resource_listdir,
1740+)
1741
1742 DOCTEST_FLAGS = (
1743- doctest.ELLIPSIS |
1744- doctest.NORMALIZE_WHITESPACE |
1745- doctest.REPORT_NDIFF)
1746+ doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF
1747+)
1748
1749
1750 def load_tests(loader, tests, pattern):
1751 """Load the doc tests (README.txt and docs/*, if any exist)."""
1752 doctest_files = [
1753- os.path.abspath(resource_filename('lazr.sshserver', 'README.txt'))]
1754- if resource_exists('lazr.sshserver', 'docs'):
1755- for name in resource_listdir('lazr.sshserver', 'docs'):
1756- if name.endswith('.txt'):
1757+ os.path.abspath(resource_filename("lazr.sshserver", "README.txt"))
1758+ ]
1759+ if resource_exists("lazr.sshserver", "docs"):
1760+ for name in resource_listdir("lazr.sshserver", "docs"):
1761+ if name.endswith(".txt"):
1762 doctest_files.append(
1763 os.path.abspath(
1764- resource_filename('lazr.sshserver', 'docs/%s' % name)))
1765+ resource_filename("lazr.sshserver", "docs/%s" % name)
1766+ )
1767+ )
1768 globs = {
1769 future_item: getattr(__future__, future_item)
1770- for future_item in ('absolute_import', 'print_function')}
1771+ for future_item in ("absolute_import", "print_function")
1772+ }
1773 kwargs = dict(
1774- module_relative=False, globs=globs, optionflags=DOCTEST_FLAGS)
1775+ module_relative=False, globs=globs, optionflags=DOCTEST_FLAGS
1776+ )
1777 atexit.register(cleanup_resources)
1778 tests.addTest(doctest.DocFileSuite(*doctest_files, **kwargs))
1779 return tests
1780diff --git a/src/lazr/sshserver/tests/test_events.py b/src/lazr/sshserver/tests/test_events.py
1781index f5fb161..a3e790a 100644
1782--- a/src/lazr/sshserver/tests/test_events.py
1783+++ b/src/lazr/sshserver/tests/test_events.py
1784@@ -10,19 +10,13 @@ __metaclass__ = type
1785 import logging
1786
1787 import testtools
1788-from zope.component import (
1789- adapter,
1790- getGlobalSiteManager,
1791- provideHandler,
1792- )
1793+
1794 # This non-standard import is necessary to hook up the event system.
1795 import zope.component.event # noqa: F401
1796+from zope.component import adapter, getGlobalSiteManager, provideHandler
1797 from zope.event import notify
1798
1799-from lazr.sshserver.events import (
1800- ILoggingEvent,
1801- LoggingEvent,
1802- )
1803+from lazr.sshserver.events import ILoggingEvent, LoggingEvent
1804
1805
1806 class ListHandler(logging.Handler):
1807@@ -48,7 +42,6 @@ class ListHandler(logging.Handler):
1808
1809
1810 class TestLoggingEvent(testtools.TestCase):
1811-
1812 def assertLogs(self, records, function, *args, **kwargs):
1813 """Assert 'function' logs 'records' when run with the given args."""
1814 logged_events = []
1815@@ -57,8 +50,12 @@ class TestLoggingEvent(testtools.TestCase):
1816 result = function(*args, **kwargs)
1817 self.logger.removeHandler(handler)
1818 self.assertEqual(
1819- [(record.levelno, record.getMessage())
1820- for record in logged_events], records)
1821+ [
1822+ (record.levelno, record.getMessage())
1823+ for record in logged_events
1824+ ],
1825+ records,
1826+ )
1827 return result
1828
1829 def assertEventLogs(self, record, logging_event):
1830@@ -79,15 +76,17 @@ class TestLoggingEvent(testtools.TestCase):
1831
1832 def test_level(self):
1833 event = LoggingEvent(logging.CRITICAL, "foo")
1834- self.assertEventLogs((logging.CRITICAL, 'foo'), event)
1835+ self.assertEventLogs((logging.CRITICAL, "foo"), event)
1836
1837 def test_formatting(self):
1838 event = LoggingEvent(logging.DEBUG, "foo: %(name)s", name="bar")
1839- self.assertEventLogs((logging.DEBUG, 'foo: bar'), event)
1840+ self.assertEventLogs((logging.DEBUG, "foo: bar"), event)
1841
1842 def test_subclass(self):
1843 class SomeEvent(LoggingEvent):
1844 template = "%(something)s happened."
1845 level = logging.INFO
1846+
1847 self.assertEventLogs(
1848- (logging.INFO, 'foo happened.'), SomeEvent(something='foo'))
1849+ (logging.INFO, "foo happened."), SomeEvent(something="foo")
1850+ )
1851diff --git a/src/lazr/sshserver/tests/test_session.py b/src/lazr/sshserver/tests/test_session.py
1852index 13a4667..aebc200 100644
1853--- a/src/lazr/sshserver/tests/test_session.py
1854+++ b/src/lazr/sshserver/tests/test_session.py
1855@@ -21,7 +21,7 @@ class MockSSHSession:
1856 self.log = log
1857
1858 def writeExtended(self, channel, data):
1859- self.log.append(('writeExtended', channel, data))
1860+ self.log.append(("writeExtended", channel, data))
1861
1862
1863 class MockProcessTransport:
1864@@ -35,16 +35,16 @@ class MockProcessTransport:
1865 self.session = MockSSHSession(self.log)
1866
1867 def closeStdin(self):
1868- self.log.append(('closeStdin',))
1869+ self.log.append(("closeStdin",))
1870
1871 def loseConnection(self):
1872- self.log.append(('loseConnection',))
1873+ self.log.append(("loseConnection",))
1874
1875 def signalProcess(self, signal):
1876- self.log.append(('signalProcess', signal))
1877+ self.log.append(("signalProcess", signal))
1878
1879 def write(self, data):
1880- self.log.append(('write', data))
1881+ self.log.append(("write", data))
1882
1883
1884 class TestDoNothing(testtools.TestCase):
1885@@ -62,24 +62,33 @@ class TestDoNothing(testtools.TestCase):
1886
1887 def test_openShellNotImplemented(self):
1888 # openShell closes the connection.
1889- protocol = MockProcessTransport('bash')
1890+ protocol = MockProcessTransport("bash")
1891 self.session.openShell(protocol)
1892 self.assertEqual(
1893- [('writeExtended', connection.EXTENDED_DATA_STDERR,
1894- 'No shells on this server.\r\n'),
1895- ('loseConnection',)],
1896- protocol.log)
1897+ [
1898+ (
1899+ "writeExtended",
1900+ connection.EXTENDED_DATA_STDERR,
1901+ "No shells on this server.\r\n",
1902+ ),
1903+ ("loseConnection",),
1904+ ],
1905+ protocol.log,
1906+ )
1907
1908 def test_windowChangedNotImplemented(self):
1909 # windowChanged raises a NotImplementedError. It doesn't matter what
1910 # we pass it.
1911- self.assertRaises(NotImplementedError,
1912- self.session.windowChanged, None)
1913+ self.assertRaises(
1914+ NotImplementedError, self.session.windowChanged, None
1915+ )
1916
1917 def test_providesISession(self):
1918 # DoNothingSession must provide ISession.
1919- self.assertTrue(ISession.providedBy(self.session),
1920- "DoNothingSession doesn't implement ISession")
1921+ self.assertTrue(
1922+ ISession.providedBy(self.session),
1923+ "DoNothingSession doesn't implement ISession",
1924+ )
1925
1926 def test_closedDoesNothing(self):
1927 # closed is a no-op.
1928@@ -87,14 +96,20 @@ class TestDoNothing(testtools.TestCase):
1929
1930 def test_execCommandNotImplemented(self):
1931 # DoNothingSession.execCommand spawns the appropriate process.
1932- protocol = MockProcessTransport('bash')
1933- command = 'cat /etc/hostname'
1934+ protocol = MockProcessTransport("bash")
1935+ command = "cat /etc/hostname"
1936 self.session.execCommand(protocol, command)
1937 self.assertEqual(
1938- [('writeExtended', connection.EXTENDED_DATA_STDERR,
1939- 'Not allowed to execute commands on this server.\r\n'),
1940- ('loseConnection',)],
1941- protocol.log)
1942+ [
1943+ (
1944+ "writeExtended",
1945+ connection.EXTENDED_DATA_STDERR,
1946+ "Not allowed to execute commands on this server.\r\n",
1947+ ),
1948+ ("loseConnection",),
1949+ ],
1950+ protocol.log,
1951+ )
1952
1953 def test_eofReceivedDoesNothingWhenNoCommand(self):
1954 # When no process has been created, 'eofReceived' is a no-op.
1955diff --git a/tox.ini b/tox.ini
1956index cc1721e..a7a7a95 100644
1957--- a/tox.ini
1958+++ b/tox.ini
1959@@ -1,5 +1,5 @@
1960 [tox]
1961-envlist =
1962+envlist =
1963 py27,py35,py36,py37,py38,py39,py310
1964
1965 [testenv]

Subscribers

People subscribed via source and target branches